在自动化测试中,当测试用例数量庞大时,单进程顺序执行的速度往往难以满足需求。pytest-xdist 是基于 pytest 的一个插件:
- 在单台机器上利用多进程并行运行测试,充分利用多核 CPU 的优势;
- 实现分布式测试,在多台机器上同时执行测试;
- 结合负载均衡策略(按用例、文件或模块分配任务),提高并行执行的效率;
- 支持自动失败重试、覆盖率统计等功能;
利用多进程并行或分布式执行测试用例,这不仅能显著缩短测试运行时间,还能提升 CI/CD 流程的效率。
安装与基本使用
安装
通过 pip 安装 pytest-xdist 非常简单:
pip install pytest-xdist
安装后,可以通过 pytest -h 查看新增的命令行选项。
基本命令
最简单的并行运行命令是使用 -n 选项:
pytest -n 4
这里 4 表示使用 4 个进程并行运行测试
如果希望自动检测系统的 CPU 核心数,并充分利用所有资源,可以使用:
pytest -n auto
简单示例
下面是一个简单的示例,如何结合 pytest-xdist 进行并行测试
示例文件:test_sample.py
import time
def test_one():
time.sleep(1)
assert True
def test_two():
time.sleep(1)
assert True
def test_three():
time.sleep(1)
assert True
def test_four():
time.sleep(1)
assert True
运行以下命令来并行执行上述 4 个测试用例:
pytest -n 4 test_sample.py
此命令会将 4 个测试用例分配到 4 个进程中同时运行,从而显著减少总体执行时间。
负载均衡策略
pytest-xdist 支持多种负载均衡策略,通过 --dist 参数进行配置:
| 策略 | 说明 |
|---|---|
--dist=load |
按测试用例均衡分配,默认策略。 |
--dist=loadscope |
按测试模块分配,确保同一模块内的用例在同一进程执行,减少数据共享冲突。 |
--dist=loadfile |
按文件级别分配测试,适用于每个文件测试之间相互独立的场景。 |
例如,按模块进行负载均衡:
pytest -n 4 --dist=loadscope
负载均衡策略可以根据项目的实际情况和测试用例的特点灵活选择:
- load 策略适合大多数测试用例都较为独立且执行时间均衡的情况,通过精细地按用例分配,能最大化利用多核 CPU 的优势。
- loadscope 策略在模块内测试共享资源或有较重初始化开销时更有优势,能够减少重复工作并控制状态影响范围
- loadfile 策略则适用于需要文件级别隔离的情况,保证每个测试文件内部的状态一致性,同时避免重复加载资源。
Selenium 并行 UI 自动化测试案例
在自动化测试中,Selenium 常用于 Web UI 测试,而 pytest-xdist 则能通过多进程并行运行测试用例,大幅缩短整体执行时间。
将二者结合使用时,需要注意以下几点:
- 独立 WebDriver 实例:由于测试在不同进程中并行执行,每个进程都必须创建自己的 WebDriver 实例,不能共享同一个浏览器驱动。
- 环境隔离:确保各个进程中的测试环境(如浏览器驱动、配置文件等)互不干扰,避免因资源竞争导致测试失败。
示例文件:test_multiple_search.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
def create_driver():
options = webdriver.ChromeOptions()
options.add_argument('--headless') # 使用无头模式
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.add_argument('--window-size=1920,1080')
return webdriver.Chrome(options=options)
@pytest.fixture(scope="function")
def driver():
driver_instance = create_driver()
yield driver_instance
driver_instance.quit()
def test_google_search(driver):
# 测试 百度 搜索
driver.get("https://www.baidu.com")
search_box = driver.find_element(By.ID, "kw")
search_box.send_keys("pytest-xdist")
search_box.submit()
driver.implicitly_wait(3)
assert "pytest-xdist" in driver.page_source
def test_bing_search(driver):
# 测试 Bing 搜索
driver.get("https://www.bing.com")
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("pytest-xdist")
search_box.submit()
driver.implicitly_wait(3)
assert "pytest-xdist" in driver.page_source
def test_duckduckgo_search(driver):
# 测试 DuckDuckGo 搜索
driver.get("https://www.duckduckgo.com")
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys("pytest-xdist")
search_box.submit()
driver.implicitly_wait(3)
assert "pytest-xdist" in driver.page_source
使用 pytest-xdist 的 -n 参数启动并行执行,例如:
pytest -n 3 test_multiple_search.py
这将启动 3 个进程,每个进程各自运行不同的测试用例,从而加速整体测试执行。
Appium 移动端测试案例
在移动端自动化测试中,Appium 是一个常用工具,而 pytest-xdist 则可以将测试用例并行执行,从而大幅缩短总的测试时间。下面以 Android 计算器应用为例,展示如何利用 pytest-xdist 并行测试 App 的功能。
示例文件:test_calculator_app.py
本示例中包含两个测试用例:一个测试加法,一个测试减法。每个测试用例通过 pytest fixture 独立创建一个 Appium WebDriver 实例,确保并行运行时互不干扰。
import pytest
from appium import webdriver
from time import sleep
@pytest.fixture(scope="function")
def driver():
# 配置 desired capabilities,根据实际情况调整 deviceName、appPackage 和 appActivity
desired_caps = {
"platformName": "Android",
"deviceName": "Android Emulator", # 或实际设备 ID
"appPackage": "com.android.calculator2",
"appActivity": ".Calculator",
"automationName": "UiAutomator2"
}
# 启动 Appium WebDriver 实例
driver_instance = webdriver.Remote("http://localhost:4723/wd/hub", desired_caps)
yield driver_instance
driver_instance.quit()
def test_addition(driver):
"""
测试计算器的加法功能,计算 2 + 3 = 5
注意:元素 ID 依据具体设备和计算器应用版本可能有所不同,请根据实际情况调整
"""
# 点击数字 2
driver.find_element_by_id("com.android.calculator2:id/digit_2").click()
# 点击加号
driver.find_element_by_id("com.android.calculator2:id/op_add").click()
# 点击数字 3
driver.find_element_by_id("com.android.calculator2:id/digit_3").click()
# 点击等号
driver.find_element_by_id("com.android.calculator2:id/eq").click()
# 等待页面更新结果
sleep(2)
# 获取结果文本(此处 ID 根据具体 App 可能不同)
result = driver.find_element_by_id("com.android.calculator2:id/formula").text
assert result == "5", f"期望结果为 5,但实际结果为 {result}"
def test_subtraction(driver):
"""
测试计算器的减法功能,计算 7 - 2 = 5
"""
# 点击数字 7
driver.find_element_by_id("com.android.calculator2:id/digit_7").click()
# 点击减号
driver.find_element_by_id("com.android.calculator2:id/op_sub").click()
# 点击数字 2
driver.find_element_by_id("com.android.calculator2:id/digit_2").click()
# 点击等号
driver.find_element_by_id("com.android.calculator2:id/eq").click()
sleep(2)
result = driver.find_element_by_id("com.android.calculator2:id/formula").text
assert result == "5", f"期望结果为 5,但实际结果为 {result}"
使用 2 个进程并行执行测试:
pytest -n 2 test_calculator_app.py
此命令会启动 2 个 worker 进程,每个测试用例都在独立进程中运行,确保每个进程都有独立的 Appium WebDriver 会话。
使用时注意事项:
- 设备资源:并行测试会同时占用多个设备或模拟器,确保设备资源充足。如果使用同一设备,可能会引起会话冲突,建议配置多台设备或多个模拟器。
- desired capabilities:如果需要同时在多台设备上执行测试,请为每个设备配置不同的
deviceName、udid,并相应调整 Appium 服务端口。
欢迎来到testingpai.com!
注册 关于