如何在Azure Pipelines中拆分参数化Pytest用例到不同并行作业
解决Azure Pipeline并行作业重复执行全部参数化Pytest用例的问题
问题背景
我有一个Pytest文件,里面包含1个参数化测试,展开后生成100个独立测试用例。我在Azure Pipeline中配置了2个并行作业,但启动后两个作业都会执行全部100个测试用例:
Job 1 ----Test 1 ----Test 2 ... ----Test 100 Job 2 ----Test 1 ----Test 2 ... ----Test 100
期望实现测试用例在两个作业间均匀分配:
Job 1 ----Test 1 ----Test 2 ... ----Test 50 Job 2 ----Test 51 ----Test 52 ... ----Test 100
当前Pipeline代码:
jobs: - job: 'QA_Pipeline' strategy: parallel: 2 pool: vm-dev-pool steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - script: | pip install pytest pip install pytest-xdist displayName: 'Install modules' - script: | pytest -n=2 --capture=tee-sys ./test_smokeTests.py --doctest-modules --junitxml=junit/test-results.xml displayName: 'Run Smoke Tests'
测试文件代码:
class Test_SmokeTests: @pytest.mark.parametrize("param1,param2", [ (a1,b1), (a2,b2), ... ... (a100,b100) ] ) def test_smokeTest(self, param1,param2): print("param1 - ", param1) print("param2 - ", param2)
注意:已能通过pytest-xdist在单个作业内分配测试用例,但无法实现跨作业拆分。
问题根源
Azure Pipeline的parallel: 2会启动两个完全独立的作业,每个作业都会完整执行定义的所有步骤。当前在每个作业中使用-n=2仅实现了单个作业内的进程并行,没有对测试用例做跨作业的范围限制,导致两个作业都重复执行了全部100个测试用例。
推荐解决方案:使用pytest-split插件实现跨作业拆分
pytest-split插件可以根据指定的总份数和当前份数,自动将测试用例均匀拆分并执行对应部分,刚好匹配Azure Pipeline的并行作业变量。
修改后的Pipeline代码
jobs: - job: 'QA_Pipeline' strategy: parallel: 2 pool: vm-dev-pool steps: - task: UsePythonVersion@0 inputs: versionSpec: '3.8' - script: | pip install pytest pip install pytest-xdist pip install pytest-split # 安装测试拆分插件 displayName: 'Install modules' - script: | pytest ./test_smokeTests.py --doctest-modules --junitxml=junit/test-results.xml \ --split $(System.JobPositionInPhase) --total $(System.TotalJobsInPhase) displayName: 'Run Smoke Tests'
关键说明
$(System.JobPositionInPhase):Azure Pipeline提供的系统变量,表示当前作业在并行作业中的序号(从1开始),第一个作业值为1,第二个为2。$(System.TotalJobsInPhase):表示并行作业的总数量,这里为2。pytest-split会自动收集所有测试用例,按照总份数均匀拆分,作业1执行前50个,作业2执行后50个。
可选:保留作业内并行
如果需要在每个作业内继续使用pytest-xdist做进程并行,只需添加-n=2参数即可:
pytest -n=2 ./test_smokeTests.py --doctest-modules --junitxml=junit/test-results.xml \ --split $(System.JobPositionInPhase) --total $(System.TotalJobsInPhase)
这样每个作业会启动2个进程并行执行分配到的50个测试用例,总共有4个进程同时运行,但不会出现重复执行的问题。
替代方案:手动拆分测试用例(无需插件)
如果不想安装额外插件,可以通过命令行手动收集并筛选测试用例:
# 修改Run Smoke Tests步骤为: - script: | # 收集所有测试用例名称 pytest --collect-only -q ./test_smokeTests.py > all_tests.txt # 计算每个作业要执行的测试数量 total_tests=$(wc -l < all_tests.txt) tests_per_job=$((total_tests / $(System.TotalJobsInPhase))) # 计算当前作业的测试范围 start_line=$((( $(System.JobPositionInPhase) - 1 ) * tests_per_job + 1)) end_line=$(( $(System.JobPositionInPhase) * tests_per_job )) # 提取当前作业要执行的测试用例 sed -n "${start_line},${end_line}p" all_tests.txt > my_tests.txt # 运行筛选后的测试用例 pytest --tb=short --junitxml=junit/test-results.xml $(cat my_tests.txt) displayName: 'Run Smoke Tests'
这种方法通过收集测试用例列表,根据作业索引计算范围并筛选,最终只执行分配到的测试用例。
内容的提问来源于stack exchange,提问作者Manish
相关产品推荐
相关产品推荐

