mark 标记是 pytest 的特点之一,功能丰富且灵活。官方不仅内置了一些标记,还允许用户创建自定义标记,使用自定义标记来分类测试并运行指定分类测试用例。

PS: 本文基于pytest 8.3.3。

自定义 mark 标记

需要在 pytest.ini 文件中自定义 mark 标记,可以如下:

[pytest]
markers =
    smoke: 标记冒烟测试用例
    process: 标记业务流程测试
	priority: 标记优先级。1代表最高优先级,数字越大优先级越小
	success: 标记成功用例
	failed: 标记失败用例
    character_values_and_boundaries: 标记字符值及边界测试

除了在 pytest.ini 自定义外,还有其它方法,这里就不介绍了,感兴趣可以访问官方文档:Working with custom markers

使用自定义标记

使用自定义标记测试用例

  1. 标记单独测试用例:
# test_module1.py 
import pytest


@pytest.mark.smoke
def test_case1():
    assert [1]

@pytest.mark.smoke
@pytest.mark.process(serial=1) # serial为1代表购物流程
def test_case2():
    assert [1]


@pytest.mark.process(serial=1)
def test_case3():
    assert [2]
  1. 标记测试组
# test_module1.py
import pytest

@pytest.mark.success
class TestCase:

    def test_case4(self):
        assert [1]

    def test_case5(self):
        assert [1]

    def test_case6(self):
        assert [2]
  1. 标记整个模块
# test_module2.py
import pytest

# 标记本模块
pytestmark = pytest.mark.failed


def test_case7():
    assert [1]


def test_case8():
    assert [1]


class TestCase:

    def test_case9(self):
        assert [1]

    def test_case10(self):
        assert [1]

    def test_case11(self):
        assert [2]

运行指定标记的测试用例

必须指定 pytest.ini 文件的路径(绝对路径或相对路径)

  1. 运行标记为 smoke 的测试用例
pytest -v C:\PythonTest\Test\ -c "Config/pytest.ini" -m smoke

运行结果:

============================ test session starts =============================
configfile: pytest.ini
collected 11 items / 9 deselected / 2 selected                                                                                                                  

Config\test_mod1.py::test_case1 PASSED                                  [ 50%]
Config\test_mod1.py::test_case2 PASSED                                  [100%]

====================== 2 passed, 9 deselected in 0.01s ======================= 
  1. 运行除了标记为 smoke 外的所有测试用例
pytest -v C:\PythonTest\Test\ -c "Config/pytest.ini" -m "not smoke"

运行结果:

============================ test session starts =============================
configfile: pytest.ini
collected 11 items / 2 deselected / 9 selected                                                                                                                   

Config\test_mod1.py::test_case3 PASSED                                  [ 11%] 
Config\test_mod1.py::TestCase::test_case4 PASSED                        [ 22%] 
Config\test_mod1.py::TestCase::test_case5 PASSED                        [ 33%] 
Config\test_mod1.py::TestCase::test_case6 PASSED                        [ 44%] 
Config\test_mod2.py::test_case7 PASSED                                  [ 55%] 
Config\test_mod2.py::test_case8 PASSED                                  [ 66%] 
Config\test_mod2.py::TestCase::test_case9 PASSED                        [ 77%] 
Config\test_mod2.py::TestCase::test_case10 PASSED                       [ 88%] 
Config\test_mod2.py::TestCase::test_case11 PASSED                       [100%] 

====================== 9 passed, 2 deselected in 0.01s ======================= 
  1. 运行标记为 process(serial=1) 的测试用例
pytest -v C:\PythonTest\Test\ -c "Config/pytest.ini" -m "process(serial=1)"

运行结果:

============================ test session starts =============================
configfile: pytest.ini
collected 11 items / 9 deselected / 2 selected                                                                                                                   

Config\test_mod1.py::test_case2 PASSED                                  [ 50%] 
Config\test_mod1.py::test_case3 PASSED                                  [100%]

====================== 2 passed, 9 deselected in 0.01s ======================= 
  1. 分别运行标记为 smoke 和标记为 success 的测试用例
pytest -v C:\PythonTest\Test\ -c "Config/pytest.ini" -m "smoke or success"

运行结果:

============================ test session starts =============================
configfile: pytest.ini
collected 11 items / 6 deselected / 5 selected                                                                                                                   

Config\test_mod1.py::test_case1 PASSED                                  [ 20%] 
Config\test_mod1.py::test_case2 PASSED                                  [ 40%] 
Config\test_mod1.py::TestCase::test_case4 PASSED                        [ 60%] 
Config\test_mod1.py::TestCase::test_case5 PASSED                        [ 80%] 
Config\test_mod1.py::TestCase::test_case6 PASSED                        [100%] 

====================== 5 passed, 6 deselected in 0.01s ======================= 
  1. 运行同时标记为 smoke 和 process(serial=1) 的测试用例
pytest -v C:\PythonTest\Test\ -c "Config/pytest.ini" -m "smoke and process(serial=1)"

运行结果:

============================ test session starts =============================

configfile: pytest.ini
collected 11 items / 10 deselected / 1 selected                                                                                                                  

Config\test_mod1.py::test_case2 PASSED                                 [100%] 

====================== 1 passed, 10 deselected in 0.01s ====================== 

传递参数给自定义标记

pytest 支持自定义标记接受位置参数和关键字参数,上文的 @pytest.mark.process(serial=1) 中 serial=1 就是关键字参数。

虽然可以传递函数对象等复杂类型,但推荐使用基本数据类型(字符串、数字等),只做标记用。

位置参数

使用方法如下:

import pytest

# 自定义标记
@pytest.mark.smoke(1, 2, 3)
def test_example(request):
	# 传递给 get_closest_marker() 的参数应该是标记名称
    marker = request.node.get_closest_marker("smoke")
    if marker:
        print(marker.args)    # 输出: (1, 2, 3)
		print(marker.args[0]) # 输出: 1
		print(marker.args[1]) # 输出: 2
		print(marker.args[2]) # 输出: 3
	assert True

关键词参数

import pytest

# 自定义标记
@pytest.mark.smoke(a=1, b=2, c=3)
def test_example(request):
    marker = request.node.get_closest_marker("smoke")
    if marker:
        print(marker.kwargs)  # 输出: {'a': 1, 'b': 2, 'c': 3}
	assert True

官方标记 mark.skip

@pytest.mark.skip 是 pytest 框架中用于跳过测试用例的官方标记。它允许开发者在特定条件下(如功能未实现、环境不支持等)跳过测试用例的执行,并在测试报告中显示跳过原因。

无条件跳过测试用例

对于暂时不会执行的测试用例,可以使用 @pytest.mark.skip 装饰:

  1. 跳过单个测试用例
import pytest

@pytest.mark.skip(reason="功能尚未实现")
def test_example():
    assert False  # 该测试不会执行

运行结果:

============================ test session starts =============================
collected 1 item                                                                                                                                                 

Test/test_mod1.py::test_example SKIPPED (功能尚未实现)                    [100%] 

============================= 1 skipped in 0.01s ============================= 
  1. 跳过测试组
import pytest

@pytest.mark.skip(reason="整个测试组暂不执行")
class TestSkipClass:
    def test_case1(self):
        assert True

    def test_case2(self):
        assert True

运行结果:

============================ test session starts =============================
collected 2 items                                                                                                                                                

Test/test_mod1.py::TestSkipClass::test_case1 SKIPPED (整个测试组暂不执行)   [ 50%] 
Test/test_mod1.py::TestSkipClass::test_case2 SKIPPED (整个测试组暂不执行)   [100%] 

============================= 2 skipped in 0.01s ============================= 
  1. 跳过模块文件
import pytest

pytestmark = pytest.mark.skip(reason="模块暂不执行")

def test_case1():
    assert True

def test_case2():
    assert True

运行结果:

============================ test session starts =============================
collected 2 items                                                                                                                                                

Test/test_mod1.py::test_case1 SKIPPED (模块暂不执行)                      [ 50%] 
Test/test_mod1.py::test_case2 SKIPPED (模块暂不执行)                      [100%] 

============================= 2 skipped in 0.01s =============================

跳过满足特定条件的测试用例

pytest.mark.skipif 常用与跳过特定平台的测试用例,同样可以装饰单独测试用例、测试组、模块文件。不过应用场景比较少。

示例跳过win32环境(针对测试机):

import pytest
import sys

# 定义通用标记:在 Windows 平台跳过
@pytest.mark.skipif(
    sys.platform == "win32",
    reason="不支持 Windows"
)
def test_case():
	assert True

动态跳过用例

相较于 pytest.mark.skipif,动态跳过用例无疑更灵活,应用场景更多。

  1. 跳过单独测试用例
import pytest

# 定义条件函数
def condition():
	return True

def test_dynamic_skip():
	if condition():  # 根据条件动态跳过
		pytest.skip("不满足条件,跳过此测试")
	assert True

运行结果:

============================ test session starts =============================
collected 1 item                                                                                                                                                 

Test/test_mod1.py::test_dynamic_skip SKIPPED (不满足条件跳过此测试)      [100%] 

============================= 1 skipped in 0.01s ============================= 
  1. 跳过测试组
import pytest

# 定义条件函数
def condition():
    return True  # 根据实际需求返回 True 或 False

class TestDynamicSkip:
    @classmethod
    def setup_class(cls):
        # 如果条件满足,则跳过整个类
        if condition():
            pytest.skip("不满足条件,跳过此测试组", allow_module_level=True)

    def test_case1(self):
        assert True  # 此测试不会执行

    def test_case2(self):
        assert True  # 此测试不会执行

运行结果:

================================== test session starts ===================================
collected 2 items                                                                                                                                                

Test/test_mod1.py::TestDynamicSkip::test_case1 SKIPPED (不满足条件跳过此测试组)                                                                         [ 50%] 
Test/test_mod1.py::TestDynamicSkip::test_case2 SKIPPED (不满足条件跳过此测试组)       [100%] 

=================================== 2 skipped in 0.01s ===================================
  1. 跳过模块文件
import pytest

# 定义条件函数
def condition():
    return True  # 根据实际需求返回 True 或 False
	
if condition():
	pytestmark = pytest.mark.skip(reason="不满足条件,模块暂不执行")
	
	
def test_case1():
    assert True

def test_case2():
    assert False

运行结果:

============================ test session starts =============================
collected 2 items                                                                                                                                                

Test/test_mod1.py::test_case1 SKIPPED (不满足条件模块暂不执行)           [ 50%] 
Test/test_mod1.py::test_case2 SKIPPED (不满足条件模块暂不执行)           [100%] 

============================= 2 skipped in 0.01s ============================= 
  1. 跳过测试套件

用例目录里的 init.py 文件里定义:

# 定义条件函数
def condition():
    return True  # 根据实际需求返回 True 或 False
	
if condition():
	pytestmark = pytest.mark.skip(reason="不满足条件,未执行测试")

满足条件后,会跳过这个目录下的所有测试用例。

官方标记 mark.xfail

它可以标记测试用例为预期失败,用于已经知道测试的功能存在问题,进行标记。当断言失败时,不会计入失败;如果断言意外通过成功,可以设置 strict=True 来将其标记为失败。

import pytest


@pytest.mark.xfail(reason="这个测试用例应该不通过", run=True)
def test_case1():
    assert False

@pytest.mark.xfail(reason="这个测试用例应该不通过")
def test_case2():
    assert False

运行结果:

============================ test session starts =============================
collected 3 items                                                                                                                                                

Test/test_mod1.py::test_case1 XFAIL (这个测试用例应该不通过)               [ 33%]
Test/test_mod1.py::test_case2 XPASS (这个测试用例应该不通过)               [ 66%] 
Test/test_mod1.py::test_case3 FAILED                                   [100%] 

================================== FAILURES ================================== 
_________________________________ test_case3 _________________________________ 
[XPASS(strict)] 这个测试用例应该不通过
========================== short test summary info =========================== 
FAILED Test/test_mod1.py::test_case3
================== 1 failed, 1 xfailed, 1 xpassed in 0.05s =================== 

编写超时处理标记

本来应该介绍第三方标记 @pytest.mark.timeout,对测试用例设置超时时间。但是此插件在 Windows 平台不会按预期运行,我也不使用 Linux 平台,所以就不介绍它了。读者如果有需求,可以查看pytest-timeout文档

如果一个测试用例的执行时间超过我的预期时间,则视为失败,这是我的需求。我完全可以实现一个简单的装饰器满足需求。

  1. 在 Package/ 目录下新建 timeout.py 文件
# Package/tool.py
import concurrent.futures
import functools

# 定义超时装饰器,接收参数 seconds(默认10秒),返回一个装饰器函数 decorator
def timeout(seconds: int=10)->callable:
    """

    :param seconds:
    :return: decorator
    """
    #  接收被装饰的函数 func,返回一个包装函数 wrapper。
    def decorator(func:callable)->callable:
        @functools.wraps(func) # 拷贝被装饰函数的元信息
        def wrapper(*args, **kwargs)-> any: # 定义包装函数及超时逻辑
            with concurrent.futures.ThreadPoolExecutor() as executor:
                future = executor.submit(func)
                try:
                    return future.result(timeout=seconds)
                except concurrent.futures.TimeoutError:
                    # 抛出 AssertionError 并携带自定义错误信息
                    error_msg = f"测试用例 {func.__name__} 已执行超过 {seconds} 秒。"
                    raise AssertionError(error_msg) from None
        return wrapper
    return decorator
  1. 使用 @timeout 装饰器
# Test/test_mod1.py
import requests

from Package.timeout import timeout


@timeout(3)
def test_mod1():
    # 想请求 http://127.0.0.1:5000/TimeOut/?sleep=5 接口,可以在 Requests 系列文章中下载
    # sleep=x代表接口x秒后响应,x需要是数字,比如5、6、11、7等等
    result = requests.get("http://127.0.0.1:5000/TimeOut/?sleep=6")
    assert result.status_code == 200, "失败"


@timeout(3)
def test_mod2():
    result = requests.get("http://127.0.0.1:5000/TimeOut/?sleep=2")
    assert result.status_code == 200, "失败"
  1. 运行结果
===================================================================== test session starts ======================================================================
collected 2 items                                                                                                                                               

Test/test_mod2.py::test_mod1 FAILED                                                                                                                       [ 50%]
Test/test_mod2.py::test_mod2 PASSED                                                                                                                       [100%]

=================================================================== short test summary info ==================================================================== 
FAILED Test/test_mod2.py::test_mod1 - AssertionError: 测试用例 test_mod1 已执行超过 3
================================================================= 1 failed, 1 passed in 8.29s ================================================================== 

第三方标记 mark.order

pytest-order 是 pytest-ordering 的分支,比较新。它可以指定测试用例的执行顺序。

安装 pytest-order :

pip install pytest-order

pytest 框架中,测试用例的定义顺序就是执行顺序(包括模块),比如:

# test_mod1.py
def test_case1():
    assert True

def test_case2():
    assert True

def test_case3():
    assert True

运行结果:

===================================================================== test session starts ======================================================================
collected 3 items                                                                                                                                                

Test/test_mod1.py::test_case1 PASSED                                                                                                                      [ 33%] 
Test/test_mod1.py::test_case2 PASSED                                                                                                                      [ 66%] 
Test/test_mod1.py::test_case3 PASSED                                                                                                                      [100%] 

====================================================================== 3 passed in 0.01s ======================================================================= 

使用 @pytest.mark.order() 标记,可以改变测试用例的执行顺序,比如:

# test_mod1.py
import pytest

@pytest.mark.order(3)
def test_case1():
    assert True
@pytest.mark.order(1)
def test_case2():
    assert True
@pytest.mark.order(2)
def test_case3():
    assert True

运行结果:

===================================================================== test session starts ======================================================================
collected 3 items                                                                                                                                                

Test/test_mod1.py::test_case2 PASSED                                                                                                                      [ 33%] 
Test/test_mod1.py::test_case3 PASSED                                                                                                                      [ 66%] 
Test/test_mod1.py::test_case1 PASSED                                                                                                                      [100%] 

====================================================================== 3 passed in 0.01s ======================================================================= 

按索引排序

与 python 索引的规则一致,比如 -1 代表最后一个执行,-2 代表倒数第二个执行,0 代表第一个执行,1 代表第二个执行。

# test_mod1.py
import pytest


@pytest.mark.order(-2)
def test_three():
    assert True


@pytest.mark.order(-1)
def test_four():
    assert True


@pytest.mark.order(2)
def test_two():
    assert True


@pytest.mark.order(1)
def test_one():
    assert True

运行结果:

===================================================================== test session starts ======================================================================
collected 4 items                                                                                                                                                

Test/test_mod1.py::test_one PASSED                                                                                                                        [ 25%] 
Test/test_mod1.py::test_two PASSED                                                                                                                        [ 50%] 
Test/test_mod1.py::test_three PASSED                                                                                                                      [ 75%] 
Test/test_mod1.py::test_four PASSED                                                                                                                       [100%] 

====================================================================== 4 passed in 0.01s ======================================================================= 

相对于其他测试用例的顺序

主要是两个参数:after(之后执行) 和 before(之前执行),如下:

# test_mod1.py
import pytest


@pytest.mark.order(after="test_second")
def test_third():
    assert True


def test_second():
    assert True


@pytest.mark.order(before="test_second")
def test_first():
    assert True

运行结果:

===================================================================== test session starts ======================================================================
collected 3 items                                                                                                                                                

Test/test_mod1.py::test_first PASSED                                                                                                                      [ 33%] 
Test/test_mod1.py::test_second PASSED                                                                                                                     [ 66%] 
Test/test_mod1.py::test_third PASSED                                                                                                                      [100%] 

====================================================================== 3 passed in 0.01s ======================================================================= 

还有其它用法,感兴趣者可以查阅pytest-order文档

提醒一下,不要滥用 order 标记,非必要不使用。

第三方标记mark.dependency

它可以定义测试用例的依赖项。简单来说,如果被依赖的测试用例未通过(状态不是passed),则设置依赖项的测试用例会被跳过。

安装 pytest-dependency:

pip install pytest-dependency

基本使用方法:

# test_mod1.py
import pytest

# name可以为测试用例起别名,方便后续依赖
@pytest.mark.dependency(name="xfail")
@pytest.mark.xfail(reason="预期失败")
def test_xfail():
    assert False

@pytest.mark.dependency(name="skip")
@pytest.mark.skip(reason="预期跳过")
def test_skip():
    assert True


@pytest.mark.dependency(name="failed")
def test_failed():
    assert  False

@pytest.mark.dependency(name="passed")
def test_passed():
    assert  True



# depends 定义被依赖项,
@pytest.mark.dependency(depends=["xfail"])
def test_a():
    assert  True

@pytest.mark.dependency(depends=["skip"])
def test_b():
    assert  True

@pytest.mark.dependency(depends=["failed"])
def test_c():
    assert  True

# 只有被依赖项状态为passed,才会执行测试用例
@pytest.mark.dependency(depends=["passed"])
def test_d():
    assert  True

# 只有多个被依赖项状态同时为passed,才会执行测试用例
@pytest.mark.dependency(depends=["passed", "failed", "xfail", "skip"])
def test_e():
    assert  True

运行结果:

===================================================================== test session starts ======================================================================
collected 9 items                                                                                                                                                

Test/test_mod1.py::test_xfail XFAIL (预期失败)                                                                                                            [ 11%] 
Test/test_mod1.py::test_skip SKIPPED (预期跳过)                                                                                                           [ 22%] 
Test/test_mod1.py::test_failed FAILED                                                                                                                     [ 33%] 
Test/test_mod1.py::test_passed PASSED                                                                                                                     [ 44%]
Test/test_mod1.py::test_a SKIPPED (test_a depends on xfail)                                                                                               [ 55%] 
Test/test_mod1.py::test_b SKIPPED (test_b depends on skip)                                                                                                [ 66%] 
Test/test_mod1.py::test_c SKIPPED (test_c depends on failed)                                                                                              [ 77%] 
Test/test_mod1.py::test_d PASSED                                                                                                                          [ 88%] 
Test/test_mod1.py::test_e SKIPPED (test_e depends on failed)                                                                                              [100%] 

====================================================== 1 failed, 2 passed, 5 skipped, 1 xfailed in 0.02s ======================================================= 

还可以在测试组中使用及依赖的同时被依赖:

# test_mod1.py
import pytest


class Test:
    @staticmethod
    @pytest.mark.dependency(name="failed")
    def test_failed():
        assert False

    @staticmethod
    @pytest.mark.dependency(name="passed")
    def test_passed():
        assert True

class Test2:
    @staticmethod
    @pytest.mark.dependency(depends=["failed"])
    def test_a():
        assert True

    @staticmethod
    @pytest.mark.dependency(name="b",depends=["passed"])
    def test_b():
        assert False


@pytest.mark.dependency(depends=["b"])
def test_c():
    assert True

运行结果:

===================================================================== test session starts ======================================================================
collected 5 items                                                                                                                                                

Test/test_mod1.py::Test::test_failed FAILED                                                                                                               [ 20%]
Test/test_mod1.py::Test::test_passed PASSED                                                                                                               [ 40%] 
Test/test_mod1.py::Test2::test_a SKIPPED (test_a depends on failed)                                                                                       [ 60%] 
Test/test_mod1.py::Test2::test_b FAILED                                                                                                                   [ 80%] 
Test/test_mod1.py::test_c SKIPPED (test_c depends on b)                                                                                                   [100%] 

============================================================ 2 failed, 1 passed, 2 skipped in 0.02s ============================================================ 

也可以跨模块使用,但是涉及的模块都要被执行:

# test_mod1.py
import pytest

@pytest.mark.dependency(name="failed")
def test_failed():
    assert False

运行结果:

===================================================================== test session starts ======================================================================
collected 2 items                                                                                                                                                

Test/test_mod1.py::test_failed FAILED                                                                                                                     [ 50%] 
Test/test_mod2.py::test_01 SKIPPED (test_01 depends on failed)                                                                                            [100%] 

================================================================= 1 failed, 1 skipped in 0.01s ================================================================= 

当然还有一些用法,感兴趣者可以查阅pytest-dependency文档

而且,不要滥用 dependency 标记,非必要不使用。测试用例的独立性是非常必要的。

还有 @pytest.mark.parametrize 会在后面的篇章中介绍。除此之外,可以查阅pytest插件列表,有很多有用的插件。

补充测试套件

把 pytest.ini 放在 Config 目录下,并把自定义的超时标记放在 Package 目录下:

Project/
├── Config/
│	└── pytest.ini            # 自定义 mark 标记
├── Package/                  # 程序目录
│   ├── __init__.py           # 包初始化文件,可以定义一些变量或执行一些操作。当然里面什么都不写也可以。
│   ├── timeout.py            # 工具模块,定义了超时标记
│   ├── module1.py            # 测试程序模块,比如连接数据库操作数据,接口请求等操作,推荐按功能封装成类
│   └── module2.py            # 测试程序模块,比如连接数据库操作数据,接口请求等操作,推荐按功能封装成类
├── Test/                     # 测试用例目录
│   ├── __init__.py           # 包初始化文件
│   ├── test_module1.py       # 测试 module1 的测试用例
│   └── test_module2.py       # 测试 module2 的测试用例
├── app.py                    # 项目启动文件
├── requirements.txt          # 项目依赖项列表
└── README.md                 # 项目说明文档

在app.py中指定 pytest.ini 的路径,并新增 -m 命令行参数:

from typing import Union

import pytest
import sys
import logging
import argparse
import os

# 自定义日志格式
LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s'
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
logger = logging.getLogger(__name__)
# 配置文件
CONFIG_PATH = "Config/pytest.ini"

def run_tests(test_target: str, testprint: str, tb: str, mark: str)->int:
    """
    运行pytest测试并返回退出码

    Args:
        test_target (str): 测试目标路径
        testprint (str): 控制输出信息的命令行参数
        tb (str): 控制输出信息的命令行参数
        mark (str): 指定运行特定标记的测试用例

    Returns:
        int: pytest退出码
    """
    if not os.path.exists(test_target):
        logger.error(f"测试目标路径不存在: {test_target}")
        return 1

    # 构建pytest参数
    pytest_args = []
    if os.path.exists(CONFIG_PATH):
        pytest_args.append("-c")
        pytest_args.append(CONFIG_PATH)
    else:
        logger.warning("未找到配置文件,将使用默认配置")
    if testprint:
        pytest_args.append("-"+ testprint)
    pytest_args.extend([
        test_target,
        "--tb=" + tb
    ])
    if mark:
        pytest_args.append("-m")
        pytest_args.append(mark)


    try:
        logger.info(f"开始运行测试,目标路径: {test_target}")
        exit_code = pytest.main(pytest_args)

        # 退出码说明映射
        exit_messages = {
            0: "✅ 全部测试用例通过",
            1: "⚠️ 部分测试用例未通过",
            2: "❌ 测试过程中有中断或其他非正常终止",
            3: "❌ 内部错误",
            4: "❌ pytest无法找到任何测试用例",
            5: "❌ pytest遇到了命令行解析错误"
        }

        logger.info(exit_messages.get(exit_code, f"❓ 未知的退出码: {exit_code}"))
        return exit_code

    except Exception as e:
        logger.exception("运行测试时发生致命错误:")
        logger.debug("异常详情:", exc_info=True)
        return 1


def parse_arguments()-> argparse.Namespace:
    """解析命令行参数"""
    parser = argparse.ArgumentParser(description="使用指定的命令运行 pytest 测试")
    parser.add_argument(
        'test_target',
        nargs='?',
        type=str,
        default="Test/",
        help='指定测试目录文件 (默认: Test/)'
    )
    parser.add_argument(
        '-p', '--testprint',
        nargs='?',
        type=str,
        default="",
        choices=["","v", "ra", "rA", "rp", "rx", "rs", "rN", "vra", "vrA", "vrp", "vrx", "vrs", "vrN"],
        help='控制输出信息'
    )
    parser.add_argument(
        '-tb', '--tb',
        nargs='?',
        type=str,
        default="auto",
        choices=["auto","long", "short", "line", "native", "no"],
        help='控制输出信息'
    )
    parser.add_argument(
        '-m', '--mark',
        nargs='?',
        type=str,
        help='控制输出信息'
    )
    return parser.parse_args()


if __name__ == "__main__":
    args = parse_arguments()

    exit_code = run_tests(args.test_target, args.testprint, args.tb, args.mark)
    sys.exit(exit_code)

项目根目录下,运行指定标记的测试用例只需要 -m + 参数即可。


THEEND



© 转载需要保留原始链接,未经明确许可,禁止商业使用。CC BY-NC-ND 4.0