# -*- coding: utf-8 -*- """ 统一测试执行文件 支持批量运行所有用例、按标签运行、按目录运行等 """ import os import sys import subprocess import argparse from pathlib import Path # 添加项目根目录到 Python 路径 current_file_path = os.path.abspath(__file__) project_root = os.path.abspath(os.path.join(os.path.dirname(current_file_path), '../../')) if project_root not in sys.path: sys.path.insert(0, project_root) # 测试目录 TEST_CASE_DIR = 'zhyy/test_case/TestCase' # 报告目录 REPORT_DIR = os.path.join(os.path.dirname(current_file_path), 'reports') ALLURE_RESULTS_DIR = os.path.join(REPORT_DIR, 'allure-results') ALLURE_REPORT_DIR = os.path.join(REPORT_DIR, 'allure-report') def ensure_dirs(): """确保报告目录存在""" os.makedirs(ALLURE_RESULTS_DIR, exist_ok=True) os.makedirs(ALLURE_REPORT_DIR, exist_ok=True) def run_pytest(args_list): """执行pytest命令""" # 基础pytest命令 # 设置PYTHONPATH环境变量,确保能导入zhyy模块 env = os.environ.copy() if 'PYTHONPATH' in env: env['PYTHONPATH'] = project_root + os.pathsep + env['PYTHONPATH'] else: env['PYTHONPATH'] = project_root # 检测是否在Jenkins环境中 is_jenkins = 'JENKINS_URL' in env or 'BUILD_NUMBER' in env if is_jenkins: print("检测到Jenkins环境,使用Jenkins配置...") # 在Jenkins中,报告路径使用绝对路径 if 'WORKSPACE' in env: workspace = env['WORKSPACE'] # 确保使用Jenkins工作空间的报告目录 global ALLURE_RESULTS_DIR, ALLURE_REPORT_DIR ALLURE_RESULTS_DIR = os.path.join(workspace, 'zhyy', 'test_case', 'reports', 'allure-results') ALLURE_REPORT_DIR = os.path.join(workspace, 'zhyy', 'test_case', 'reports', 'allure-report') ensure_dirs() cmd = ['python', '-m', 'pytest'] + args_list print("=" * 80) print("执行命令: {}".format(' '.join(cmd))) print("命令列表: {}".format(cmd)) print("测试目录: {}".format(TEST_CASE_DIR)) print("测试目录是否存在: {}".format(os.path.exists(TEST_CASE_DIR))) if is_jenkins: print("Jenkins工作空间: {}".format(env.get('WORKSPACE', 'N/A'))) print("构建编号: {}".format(env.get('BUILD_NUMBER', 'N/A'))) print("PYTHONPATH: {}".format(env.get('PYTHONPATH'))) print("Allure结果目录: {}".format(ALLURE_RESULTS_DIR)) print("当前工作目录: {}".format(os.getcwd())) print("=" * 80) # 使用 shell=True 来执行命令,确保能正确处理路径和参数 result = subprocess.run(' '.join(cmd), shell=True, cwd=project_root, env=env) return result.returncode def find_test_files(directory): """递归查找所有测试文件""" test_files = [] for root, dirs, files in os.walk(directory): for file in files: if file.endswith('.py'): # 检查文件中是否包含测试函数 file_path = os.path.join(root, file) try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() if 'def test_' in content or 'class Test' in content: test_files.append(file_path) except: pass return test_files def run_all_tests(): """运行所有测试用例""" print("运行所有测试用例...") # 递归查找所有测试文件 test_files = find_test_files(TEST_CASE_DIR) if not test_files: print("未找到测试文件") return 1 print(f"找到 {len(test_files)} 个测试文件:") for file in test_files: print(f" - {file}") args = [ *test_files, '-v', # 详细输出 '--tb=short', # 简短的错误信息 f'--alluredir={ALLURE_RESULTS_DIR}', # allure结果目录 ] return run_pytest(args) def run_by_feature(feature_name): """按allure feature标签运行""" print("按feature标签运行: {}".format(feature_name)) args = [ TEST_CASE_DIR, '-v', '--tb=short', f'--allure-features={feature_name}', f'--alluredir={ALLURE_RESULTS_DIR}', ] return run_pytest(args) def run_by_story(story_name): """按allure story标签运行""" print("按story标签运行: {}".format(story_name)) args = [ TEST_CASE_DIR, '-v', '--tb=short', f'--allure-stories={story_name}', f'--alluredir={ALLURE_RESULTS_DIR}', ] return run_pytest(args) def run_by_marker(marker): """按pytest标记运行""" print("按pytest标记运行: {}".format(marker)) args = [ TEST_CASE_DIR, '-v', '--tb=short', f'-m={marker}', f'--alluredir={ALLURE_RESULTS_DIR}', ] return run_pytest(args) def run_by_dir(directory): """按目录运行""" full_path = os.path.join(TEST_CASE_DIR, directory) if not os.path.exists(full_path): print("错误: 目录不存在: {}".format(full_path)) return 1 print("按目录运行: {}".format(directory)) args = [ full_path, '-v', '--tb=short', f'--alluredir={ALLURE_RESULTS_DIR}', ] return run_pytest(args) def run_by_file(file_path): """按文件运行""" full_path = os.path.join(TEST_CASE_DIR, file_path) if not os.path.exists(full_path): print("错误: 文件不存在: {}".format(full_path)) return 1 print("按文件运行: {}".format(file_path)) args = [ full_path, '-v', '--tb=short', f'--alluredir={ALLURE_RESULTS_DIR}', ] return run_pytest(args) def run_by_keyword(keyword): """按关键字运行(匹配文件名或类名)""" print("按关键字运行: {}".format(keyword)) args = [ TEST_CASE_DIR, '-v', '--tb=short', f'-k={keyword}', f'--alluredir={ALLURE_RESULTS_DIR}', ] return run_pytest(args) def generate_allure_report(): """生成allure报告""" print("生成Allure报告...") cmd = [ 'allure', 'generate', ALLURE_RESULTS_DIR, '-o', ALLURE_REPORT_DIR, '--clean' ] try: # 使用shell=True来执行命令,确保使用正确的环境变量 result = subprocess.run(' '.join(cmd), shell=True, check=True) print("Allure报告生成成功: {}".format(ALLURE_REPORT_DIR)) print("打开报告命令: allure open {}".format(ALLURE_REPORT_DIR)) return 0 except subprocess.CalledProcessError as e: print("生成Allure报告失败: {}".format(e)) return 1 except FileNotFoundError: print("警告: 未找到allure命令,请先安装allure") print("安装方法: https://docs.qameta.io/allure/") return 1 def open_allure_report(): """打开allure报告""" print("打开Allure报告...") cmd = ['allure', 'open', ALLURE_REPORT_DIR] try: # 使用shell=True来执行命令,确保使用正确的环境变量 subprocess.Popen(' '.join(cmd), shell=True) print("Allure报告已在浏览器中打开") except FileNotFoundError: print("警告: 未找到allure命令,请先安装allure") def main(): parser = argparse.ArgumentParser( description='统一测试执行工具', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 使用示例: # 运行所有测试 python run_tests.py --all # 按feature标签运行 python run_tests.py --feature "深圳采购工作台采购订单页面" # 按story标签运行 python run_tests.py --story "验证采购工作台采购订单页面列表查询" # 按目录运行 python run_tests.py --dir "接口/SZPurchase" # 按文件运行 python run_tests.py --file "接口/SZPurchase/PurchaseOrderManage.py" # 按关键字运行 python run_tests.py --keyword "purchase" # 按pytest标记运行 python run_tests.py --marker "smoke" # 生成并打开报告 python run_tests.py --all --report --open """ ) # 运行选项(不强制要求,默认运行所有测试) run_group = parser.add_mutually_exclusive_group(required=False) run_group.add_argument('--all', action='store_true', help='运行所有测试用例(默认)') run_group.add_argument('--feature', type=str, help='按allure feature标签运行') run_group.add_argument('--story', type=str, help='按allure story标签运行') run_group.add_argument('--dir', type=str, help='按目录运行(相对于TestCase目录)') run_group.add_argument('--file', type=str, help='按文件运行(相对于TestCase目录)') run_group.add_argument('--keyword', type=str, help='按关键字运行(匹配文件名或类名)') run_group.add_argument('--marker', type=str, help='按pytest标记运行') # 报告选项 parser.add_argument('--report', action='store_true', help='生成Allure报告') parser.add_argument('--open', action='store_true', help='打开Allure报告(需要先使用--report)') parser.add_argument('--no-report', action='store_true', help='不生成Allure报告(默认会生成)') args = parser.parse_args() # 确保目录存在 ensure_dirs() # 执行测试(如果没有指定选项,默认运行所有测试) exit_code = 0 if args.feature: exit_code = run_by_feature(args.feature) elif args.story: exit_code = run_by_story(args.story) elif args.dir: exit_code = run_by_dir(args.dir) elif args.file: exit_code = run_by_file(args.file) elif args.keyword: exit_code = run_by_keyword(args.keyword) elif args.marker: exit_code = run_by_marker(args.marker) else: # 默认运行所有测试 exit_code = run_all_tests() # 生成报告 if args.report or (not args.no_report and exit_code == 0): generate_allure_report() if args.open: open_allure_report() print("=" * 80) if exit_code == 0: print("✓ 测试执行完成") else: print("✗ 测试执行失败,退出码: {}".format(exit_code)) print("=" * 80) sys.exit(exit_code) if __name__ == '__main__': main()