zoukankan      html  css  js  c++  java
  • pytest, Allure

    本文参考链接:

    https://learning-pytest.readthedocs.io/zh/latest/

    https://blog.csdn.net/qq_42610167/article/details/101204066#8pytesthtml_773

    https://qualitysphere.github.io/4_allure/

    快速入门

    测试文件以 test_ 开头或以 _test 结尾。

    测试类以 Test 开头,并且不能有 __init__ 方法

    测试函数以 test_ 开头

    安装 Pytest

    使用 pip 进行安装:

    $ pip install pytest
    

    第一个测试函数

    Pytest 使用 Python 的 assert 进行条件判断,最简单的测试函数如:

    # test1.py
    
    def test_passing():
        assert (1, 2, 3) == (1, 2, 3)
    

    运行测试函数

    使用命令 pytest 运行测试函数:

    $ pytest tests/test1.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    tests	est1.py .                                                         [100%]
    
    ========================== 1 passed in 0.09 seconds ===========================
    

    注解

    pytest 使用 . 标识测试成功(PASSED)。

    小技巧

    可以使用 -v 选项,显示测试的详细信息。

    使用 pytest -h 查看 pytest 的所有选项。

    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0 -- c:anaconda3python.exe
    cachedir: .pytest_cache
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    tests/test1.py::test_passing PASSED                                      [100%]
    
    ========================== 1 passed in 0.03 seconds ===========================
    

    测试失败

    下面是一个失败的测试函数:

    # test2.py
    
    def test_failing():
        assert (1, 2, 3) == (3, 2, 1)
    

    运行结果为:

    $ pytest tests/test2.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    tests	est2.py F                                                         [100%]
    
    ================================== FAILURES ===================================
    ________________________________ test_failing _________________________________
    
        def test_failing():
    >       assert (1, 2, 3) == (3, 2, 1)
    E       assert (1, 2, 3) == (3, 2, 1)
    E         At index 0 diff: 1 != 3
    E         Use -v to get the full diff
    
    tests	est2.py:2: AssertionError
    ========================== 1 failed in 0.19 seconds ===========================
    

    注解

    pytest 使用 F 标识测试失败(FAILED)。

    pytest 对失败的测试给出了非常人性化的提示。

    断言

    在 pytest 中,assert 是最常见的断言工具。如:

    assert a == b
    
    assert a <= b
    

    具体的 assert 语法参考 The assert statement

    捕获异常

    在测试过程中,经常需要测试是否如期抛出预期的异常,以确定异常处理模块生效。在 pytest 中使用 pytest.raises() 进行异常捕获:

    # test_raises.py
    
    def test_raises():
        with pytest.raises(TypeError) as e:
            connect('localhost', '6379')
        exec_msg = e.value.args[0]
        assert exec_msg == 'port type must be int'
    

    运行结果如下:

    $ pytest test_raises.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    tests	est-function	est_raise.py .                                      [100%]
    
    ========================== 1 passed in 0.07 seconds ===========================
    

    指定测试

    默认情况下,pytest 会递归查找当前目录下所有以 test 开始或结尾的 Python 脚本,并执行文件内的所有以 test 开始或结束的函数和方法。

    对于下面脚本:

    # test_no_mark.py
    
    def test_func1():
        assert 1 == 1
    
    def test_func2():
        assert 1 != 1
    

    直接执行测试脚本会同时执行所有测试函数:

    $ pytest tests/test-function/test_no_mark.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items
    
    tests	est-function	est_no_mark.py .F                                   [100%]
    
    ================================== FAILURES ===================================
    _________________________________ test_func2 __________________________________
    
        def test_func2():
    >       assert 1 != 1
    E       assert 1 != 1
    
    tests	est-function	est_no_mark.py:6: AssertionError
    ===================== 1 failed, 1 passed in 0.07 seconds ======================
    

    选择测试函数

    由于某种原因(如 test_func2 的功能尚未开发完成),我们只想执行指定的测试函数。在 pytest 中有几种方式可以解决:

    第一种,节点ID运行测试,通过 :: 标记。

    每个收集的测试都被分配一个唯一的 nodeid 它由模块文件名和诸如类名、函数名和参数化参数等说明符组成,用 :: 字符。

    $ pytest tests/test-function/test_no_mark.py::test_func1
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    tests	est-function	est_no_mark.py .                                    [100%]
    
    ========================== 1 passed in 0.02 seconds ===========================
    

    第二种,使用模糊匹配,标记关键字。使用 -k 选项标识。

    $ pytest -k func1 tests/test-function/test_no_mark.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items / 1 deselected
    
    tests	est-function	est_no_mark.py .                                    [100%]
    
    =================== 1 passed, 1 deselected in 0.03 seconds ====================
    

    上面只会运行带有 func1 关键字的测试函数

    第三种,选择文件夹

    pytest testing/
    

    第四种,使用 pytest.mark 在函数上进行标记。

    带标记的测试函数如:

    # test_with_mark.py
    import pytest
    
    @pytest.mark.finished # 通过mark来打上标记,finished 需要在 pytest.ini 里面注册,否则会报 warning
    def test_func1():
        assert 1 == 1
    
    @pytest.mark.unfinished
    def test_func2():
        assert 1 != 1
    

    同时,需要在同目录下创建一个 pytest.ini 文件,注册 mark 选项,否则上面的代码会提示warning

    [pytest]
    markers =
        finished: mark a test as finished.
        unfinished: mark a test as unfinished.
    

    上面的 finished 是标记的名字,冒号后面的描述是可选项

    测试时使用 -m 选择标记的测试函数:

    $ pytest -m finished tests/test-function/test_with_mark.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items / 1 deselected
    
    tests	est-function	est_with_mark.py .                                  [100%]
    
    =================== 1 passed, 1 deselected in 0.10 seconds ====================
    

    使用 mark,我们可以给每个函数打上不同的标记,测试时指定就可以允许所有被标记的函数。

    一个函数可以打多个标记;多个函数也可以打相同的标记。

    运行测试时使用 -m 选项可以加上逻辑,如:

    $ pytest -m "finished and unfinished"
    
    $ pytest -m "finished and not unfinished"
    

    跳过测试

    上一节提到 pytest 使用标记过滤测试函数,所以对于那些尚未开发完成的测试,最好的处理方式就是略过而不执行测试。

    按正向的思路,我们只要通过标记指定要测试的就可以解决这个问题;但有时候的处境是我们能进行反向的操作才是最好的解决途径,即通过标记指定要跳过的测试。

    1. pytest.mark.skip

    pytest.mark.skip 可以无条件跳过任何测试

    # test_skip.py
    import pytest
    
    @pytest.mark.skip(reason='out-of-date api')
    def test_connect():
        pass
    

    执行结果可以看到该测试已被忽略:

    $ pytest tests/test-function/test_skip.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    tests	est-function	est_skip.py s                                       [100%]
    
    ========================== 1 skipped in 0.13 seconds ==========================
    

    pytest 使用 s 表示测试被跳过(SKIPPED)。

    2. pytest.mark.skipif

    Pytest 还支持使用 pytest.mark.skipif 为测试函数指定被忽略的条件。

    import pytest
    
    @pytest.mark.skipif(2<3,reason='2 is smaller than 3, so skip this.')
    def test_api():
        pass
    

    3. pytest.skip()

    在函数内部进行判断,也可以跳过

    def test_function():
        if not valid_config():
            pytest.skip("unsupported configuration")
    

    也可以跳过整个模块,使用 pytest.skip(reason, allow_module_level=True) 在模块级别,这样,可以跳过整个模块的所有测试:

    import sys
    import pytest
    
    if not sys.platform.startswith("win"):
        pytest.skip("skipping windows-only tests", allow_module_level=True)
        
    def test_one():
        assert 1 == 1
    

    执行结果:

    =================== test session starts ==========================
    platform win32 -- Python 3.7.1, pytest-6.2.2, py-1.9.0, pluggy-0.13.1
    rootdir: C:UserswztshineDesktoppyt, configfile: pytest.ini
    collected 0 items / 1 skipped
    

    4. 导入跳过 importorskip

    如果缺少某些导入,则跳过模块中的所有测试:

    req = pytest.importorskip("requests")
    

    预见失败

    1. 预见失败 : xfail

    xfail可以预见测试的失败。譬如说你认为这个测试应该失败,你给测试打上 xfail 以后,如果测试真的失败了,结果是: x: xfailed; 而如果你预计错了,测试成功了,结果是:X: xpassed.

    Pytest 使用 pytest.mark.xfail 实现预见错误功能:

    # test_xfail.py
    import pytest
    
    # 预计失败
    @pytest.mark.xfail
    def test_fail():
        assert 3 == 4
    
    def test_fail2():
        if 3 != 4:
            pytest.xfail('3 != 4') # 也可以用在函数内部
    

    执行结果:

    $ pytest tests/test-function/test_xfail.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    tests	est-function	est_xfail.py xx                                      [100%]
    
    ========================== 2 xfailed in 0.12 seconds ==========================
    

    pytest 使用小写的 x 表示预见的失败(xfailed)。

    如果预见的是失败,但实际运行测试却成功通过,pytest 使用大写的 X 进行标记(xpassed)。

    condition 参数

    如果测试只在特定条件下失败,则可以将该条件作为第一个参数通过:

    import pytest
    
    @pytest.mark.xfail(2==2,reason='failed.')
    def test_one():
        assert 2 == 2
    

    reason 参数

    可以使用 reason 参数:

    @pytest.mark.xfail(reason="known parser issue")
    def test_function():
        ...
    

    raises 参数

    如果要更具体地说明测试失败的原因,可以在 raises 参数。

    @pytest.mark.xfail(raises=RuntimeError)
    def test_function():
        ...
    

    如果测试失败,则报告为常规失败,除非 raises .

    run 参数

    直接不执行这种 xfail 的测试

    @pytest.mark.xfail(run=False)
    def test_function():
        ...
    

    strict 参数

    XFAILXPASS 默认情况下都没让测试失败(默认结果是x,而失败是f)。您可以通过设置 strict 关键字参数到 True ,让它们强制失败:

    @pytest.mark.xfail(strict=True)
    def test_function():
        ...
    

    这将使测试的(“xpassed”)结果失败掉。(xfail会有两种结果,一种xfailed,一种xpassed,这个strict参数会让 xpassed的 变成 failed)

    您可以更改 strict 参数使用 xfail_strict ini选项:

    [pytest]
    xfail_strict=true
    

    忽略失败

    通过在命令行上指定:

    pytest --runxfail
    

    这样就相当于没有给测试使用 xfail 装饰器。会将它们当成正常测试来执行。

    参数化时预计失败

    import sys
    import pytest
    
    
    @pytest.mark.parametrize(
        ("n", "expected"),
        [
            (1, 2),
            pytest.param(1, 0, marks=pytest.mark.xfail),
            pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")), # 单独给这个参数加上预计失败选项
            (2, 3),
            (3, 4),
            (4, 5),
            pytest.param(
                10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")
            ),
        ],
    )
    def test_increment(n, expected):
        assert n + 1 == expected
    

    2. 失败后停止

    在第一(n)次失败后停止测试过程:

    pytest -x           # 第一次失败后停止
    pytest --maxfail=2  # 第二次失败后停止
    

    参数化

    当对一个测试函数进行测试时,通常会给函数传递多组参数。比如测试账号登陆,我们需要模拟各种千奇百怪的账号密码。

    在 pytest 中,我们有参数化测试,即每组参数都独立执行一次测试。使用的工具就是 pytest.mark.parametrize(argnames, argvalues)argnames 是一个字符串,多个参数可以用 , 隔开;argvalues 是一个列表,列表中的每个元素都会参与一次测试。

    1. 一个参数

    这里是一个密码长度的测试函数,其中参数名为 passwd,其可选列表包含三个值:

    # test_parametrize.py
    import pytest
    
    @pytest.mark.parametrize('passwd',    # 参数名,字符串格式
                          ['123456',      # 列表,每个元素测试一次,此处会测试3次。
                           'abcdefdfs',
                           'as52345fasdf4'])
    def test_passwd_length(passwd):
        assert len(passwd) >= 8
    

    运行可知执行了三次测试:

    $ pytest tests/test-function/test_parametrize.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 3 items
    
    tests	est-function	est_parametrize.py F..                              [100%]
    
    ================================== FAILURES ===================================
    

    2. 多个参数

    # test_parametrize.py
    import pytest
    import hashlib
    
    @pytest.mark.parametrize('user, passwd',		# 多个参数,用逗号隔开
                             [('jack', 'abcdefgh'), # 列表的每个元素用元组表示,元组的第一个值代表第一个参数
                              ('tom', 'a123456a')])
    def test_passwd_md5(user, passwd):
        db = {
            'jack': 'e8dc4081b13434b45189a720b77b6818',
            'tom': '1702a132e769a623c1adb78353fc9503'
        }
        assert hashlib.md5(passwd.encode()).hexdigest() == db[user]
    

    使用 -v 执行测试

    $ pytest -v tests/test-function/test_parametrize.py::test_passwd_md5
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0 -- c:anaconda3python.exe
    cachedir: .pytest_cache
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items
    
    tests/test-function/test_parametrize.py::test_passwd_md5[jack-abcdefgh] PASSED [ 50%]
    tests/test-function/test_parametrize.py::test_passwd_md5[tom-a123456a] PASSED [100%]
    
    ========================== 2 passed in 0.04 seconds ===========================
    

    如果觉得每组测试的默认参数显示不清晰,我们可以使用 pytest.paramid 参数进行自定义。

    # test_parametrize.py
    import pytest
    
    @pytest.mark.parametrize('user, passwd',
                             [pytest.param('jack', 'abcdefgh', id='User<Jack>'),
                              pytest.param('tom', 'a123456a', id='User<Tom>')])
    def test_passwd_md5_id(user, passwd):
        db = {
            'jack': 'e8dc4081b13434b45189a720b77b6818',
            'tom': '1702a132e769a623c1adb78353fc9503'
        }
    
        import hashlib
    
        assert hashlib.md5(passwd.encode()).hexdigest() == db[user]
    

    现在的执行结果为:

    $ pytest -v tests/test-function/test_parametrize.py::test_passwd_md5_id
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0 -- c:anaconda3python.exe
    cachedir: .pytest_cache
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items
    
    tests/test-function/test_parametrize.py::test_passwd_md5_id[User<Jack>] PASSED [ 50%]
    tests/test-function/test_parametrize.py::test_passwd_md5_id[User<Tom>] PASSED [100%]
    
    ========================== 2 passed in 0.07 seconds ===========================
    

    3. 参数的所有组合

    多个参数可以堆叠,产生所有参数的组合

    # test_parametrize.py
    import pytest
    
    @pytest.mark.parametrize('x',[0,1])
    @pytest.mark.parametrize('y',[0,1])
    @pytest.mark.parametrize('z',[0,1])
    def test_passwd_length(x,y,z):
        print(x,y,z)
    
    if __name__=="__main__":
        pytest.main(["-s","test_parametrize.py"])
    

    执行结果:

    $ pytest -s tests/test-function/test_parametrize.py
    ================================= test session starts =================================
    platform win32 -- Python 3.7.1, pytest-6.2.2, py-1.9.0, pluggy-0.13.1
    rootdir: C:UserswztshineDesktoppyt, configfile: pytest.ini
    collected 8 itemss
    
    test_parametrize.py 0 0 0
    .1 0 0
    .0 1 0
    .1 1 0
    .0 0 1
    .1 0 1
    .0 1 1
    .1 1 1
    .
    
    ================================== 8 passed in 0.04s ==================================
    

    -s 参数可以输出打印信息,不加 -s,print() 无法打印出来

    Fixture

    1. 简介

    Fixture 是一些函数,pytest 会在执行测试函数之前(或之后)加载运行它们,和 unittest 的fixture类似,都是做测试环境的前后梳理工作。譬如你想测试一个浏览器页面,那你肯定需要事先打开浏览器,测试完成后关闭浏览器,这些 事先事后 处理的工作,可以给fixutre来做,因为 fixture 可以在多个测试用例中重复使用。不同的是,unittest 的fixture如 setup,teardown 通常只在当前py文件中,供多个测试函数或使用,而 pytest 的fixture 可以写在一个 conftest.py 文件中,任意多个py文件中的任意测试函数,都可以直接调用,实现多个文件重复使用。

    Pytest 使用 pytest.fixture() 定义fixture,下面是最简单的fixture,只返回北京邮编:

    # test_postcode.py
    import pytest
    
    @pytest.fixture()
    def postcode():
        return '010'
    
    def test_postcode(postcode):  # 可以直接作为参数传递进来
        assert postcode == '010'
    

    fixture可以直接定义在各测试脚本中,就像上面的例子。更多时候,我们希望一个fixture可以在更大程度上复用,这就需要对fixture进行集中管理。Pytest 使用文件 conftest.py 集中管理fixture。

    重要:

    在复杂的项目中,可以在不同的目录层级定义 conftest.py,每一个 conftest.py 的作用域为其所在的目录和子目录。

    不要自己导入 conftest.py 文件,pytest 会自动识别该文件

    测试用例执行前,会自动加载当前目录,以及父级目录外的所有 conftest.py

    2. 预处理和后处理

    很多时候需要在测试前进行预处理(如新建数据库连接),并在测试完成进行清理(关闭数据库连接)。

    当有大量重复的这类操作,最佳实践是使用fixture来自动化所有预处理和后处理。

    Pytest 使用 yield 关键词将fixture分为两部分,yield 之前的代码属于预处理,会在测试前执行;yield 之后的代码属于后处理,将在测试完成后执行。

    以下测试模拟数据库查询,使用fixture来模拟数据库的连接关闭:

    # test_db.py
    import pytest
    
    @pytest.fixture()
    def db():
        print('Connection successful')
    
        yield
    
        print('Connection closed')
    
    
    def search_user(user_id):
        d = {
            '001': 'xiaoming'
        }
        return d[user_id]
    
    
    def test_search(db):
        assert search_user('001') == 'xiaoming'
    

    执行时使用 -s 阻止消息被吞:不加 -s ,无法成功 print()

    $ pytest -s tests/fixture/test_db.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    testsfixture	est_db.py Connection successful
    .Connection closed
    
    
    ========================== 1 passed in 0.02 seconds ===========================
    

    可以看到在测试成功的 . 标识前后有数据库的连接和关闭操作。

    如果想更细的跟踪fixture执行,可以使用 --setup-show 选项,它可以更详细的显示测试环境是如何搭建的:

    $ pytest --setup-show tests/fixture/test_db.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    testsfixture	est_db.py
          SETUP    F db
            tests/fixture/test_db.py::test_search (fixtures used: db).
          TEARDOWN F db
    
    ========================== 1 passed in 0.03 seconds ===========================
    

    3. 作用域

    fixture的作用是为了抽离出重复的工作和方便复用,为了更精细化控制fixture(比如只想对数据库访问测试脚本使用自动连接关闭的fixture),pytest 使用作用域来进行指定fixture的使用范围。

    在定义fixture时,通过 scope 参数声明作用域,可选项有:

    • function: 函数级,每个测试函数都会执行一次fixture;
    • class: 类级别,每个测试类执行一次,所有方法都可以使用;
    • module: 模块级,每个模块执行一次,模块内函数和方法都可使用;
    • session: 会话级,一次测试只执行一次,所有被找到的函数和方法都可用(也就是无论加载多少个模块,多少类,都只执行一次)。

    默认的作用域为 function

    import pytest
    
    @pytest.fixture(scope='function')
    def func_scope():
        pass
    
    
    @pytest.fixture(scope='module')
    def mod_scope():
        pass
    
    
    @pytest.fixture(scope='session')
    def sess_scope():
        pass
    
    
    @pytest.fixture(scope='class')
    def class_scope():
        pass
    

    最简单使用fixture方式是作为测试函数参数:

    # test_scope.py
    
    def test_multi_scope(sess_scope, mod_scope, func_scope):
        pass
    

    执行结果如下,可以清楚看到各fixture的作用域和执行顺序:

    $ pytest --setup-show tests/fixture/test_scope.py::test_multi_scope
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    testsfixture	est_scope.py
    SETUP    S sess_scope
      SETUP    M mod_scope
          SETUP    F func_scope
            tests/fixture/test_scope.py::test_multi_scope (fixtures used: func_scope, mod_scope, sess_scope).
          TEARDOWN F func_scope
      TEARDOWN M mod_scope
    TEARDOWN S sess_scope
    
    ========================== 1 passed in 0.10 seconds ===========================
    

    对于类使用作用域,需要使用 pytest.mark.usefixtures (对函数和方法也适用):

    # test_scope.py
    import pytest
    
    @pytest.mark.usefixtures('class_scope')
    class TestClassScope:
        def test_1(self):
            pass
    
        def test_2(self):
            pass
    

    执行结果如下,可见所有测试函数都在fixture作用范围内:

    $ pytest --setup-show tests/fixture/test_scope.py::TestClassScope
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items
    
    testsfixture	est_scope.py
        SETUP    C class_scope
            tests/fixture/test_scope.py::TestClassScope::()::test_1 (fixtures used: class_scope).
            tests/fixture/test_scope.py::TestClassScope::()::test_2 (fixtures used: class_scope).
        TEARDOWN C class_scope
    
    ========================== 2 passed in 0.03 seconds ===========================
    

    对于类下的所有测试函数,可以在类上面添加 fixture 来实现对所有函数的处理:

    下面的例子来源于 CSDN:
    腾哥儿的测试之路
    https://blog.csdn.net/weixin_45451320/article/details/113836073

    # test_scope.py
    # coding=utf-8
    import pytest
    
    @pytest.fixture()
    def test_case_3():
        print('---3号用例完成---')
    
    @pytest.fixture()
    def test_case_4():
        print('---4号用例完成---')
    
    @pytest.fixture()
    def test_case_5():
        print('---5号用例完成---')
    
    @pytest.fixture()
    def test_case_6():
        print('---6号用例完成---')
    
    @pytest.fixture()
    def test_case_7():
        print('---7号用例完成---')
    
    # (1)这里按照【从下到上的顺序】,执行优先级是3、4、5
    @pytest.mark.usefixtures('test_case_5')
    @pytest.mark.usefixtures('test_case_4')
    @pytest.mark.usefixtures('test_case_3')
    class Testlogin001:
    
        def test_case_1(self):
            print('---1号用例完成---')
    
        # (2)这里按照调用了前面的函数test_case_6,局部的调用,执行优先级是最高的。
        @pytest.mark.usefixtures('test_case_7')
        @pytest.mark.usefixtures('test_case_6')
        def test_case_2(self):
            print('---2号用例完成---')
    
    
    if __name__ == "__main__":
        pytest.main(['-vs', 'test_scope.py'])
    

    4. 自动执行 autouse

    目前为止,所有fixture的使用都是手动指定:要么作为参数,要么使用 usefixtures

    如果我们想让fixture自动执行,可以在定义时指定 autouse 参数。

    下面是两个自动计时fixture,一个用于统计每个函数运行时间(function 作用域),一个用于计算测试总耗时(session 作用域):

    # test_autouse.py
    import pytest
    
    DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
    
    
    @pytest.fixture(scope='session', autouse=True)
    def timer_session_scope():
        start = time.time()
        print('
    start: {}'.format(time.strftime(DATE_FORMAT, time.localtime(start))))
    
        yield
    
        finished = time.time()
        print('finished: {}'.format(time.strftime(DATE_FORMAT, time.localtime(finished))))
        print('Total time cost: {:.3f}s'.format(finished - start))
    
    
    @pytest.fixture(autouse=True)
    def timer_function_scope():
        start = time.time()
        yield
        print(' Time cost: {:.3f}s'.format(time.time() - start))
    

    注意下面的两个测试函数并都没有显式使用fixture:

    def test_1():
        time.sleep(1)
    
    
    def test_2():
        time.sleep(2)
    

    执行测试可看到,fixture自动执行并完成计时任务:

    $ pytest -s tests/fixture/test_autouse.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items
    
    testsfixture	est_autouse.py
    start: 2018-06-12 10:16:27
    . Time cost: 1.003s.
    . Time cost: 2.003s.
    finished: 2018-06-12 10:16:30
    Total time cost: 3.016s.
    
    
    ========================== 2 passed in 3.11 seconds ===========================
    

    5. 重命名

    fixture的名称默认为定义时的函数名,如果不想使用默认,可以通过 name 选项指定名称:

    # test_rename.py
    import pytest
    
    @pytest.fixture(name='age')
    def calculate_average_age():
        return 28
    
    def test_age(age):
        assert age == 28
    

    6. 参数化

    因为 fixture 也是函数,我们同样可以对 fixture 进行参数化。在什么情况下需要对 fixture 参数化?

    假设现在有一批 API 需要测试对不同数据库的支持情况(对所有数据库进行相同操作),最简单的方法就是针对每个数据库编写一个测试用例,但这包含大量重复代码,如数据库的连接、关闭,查询等。

    进一步,可以使用fixture抽离出数据库的通用操作,每个 API 都能复用这些数据库 fixture,同时可维护性也得到提升。

    更进一步,可以继续将这些 fixture 合并为一个,而通过参数控制连接到不同的数据库。这就需要使用fixture参数化来实现。fixture参数化需要使用 pytest 内置的 request,并通过 request.param 获取参数。

    import pytest 
    
    @pytest.fixture(params=[('redis', '6379'),('elasticsearch', '9200')])
    def param(request):				# 4. 执行此 fixture 的第一个参数
        return request.param
    
    @pytest.fixture(autouse=True)   # 2. 自动调用此fixture,因为 autouse=True
    def db(param):					# 3. 调用了 param 这个fixture
        print('
    Succeed to connect %s:%s' % param) 	# 5. 从param返回后,执行
        yield						# 6. 从此处又返回到 test_api 了
        print('
    Succeed to close %s:%s' % param)		# 8. 继续执行
    
    def test_api():		# 1. 执行此测试
        assert 1 == 1	# 7. 执行断言
    

    执行结果:

    $ pytest -s tests/fixture/test_parametrize.py
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 2 items
    
    testsfixture	est_parametrize.py
    Succeed to connect redis:6379
    .
    Succeed to close redis:6379
    
    Succeed to connect elasticsearch:9200
    .
    Succeed to close elasticsearch:9200
    
    ========================== 2 passed in 0.10 seconds ===========================
    

    与函数参数化使用 @pytest.mark.parametrize 不同,fixture在定义时使用 params 参数进行参数化。

    fixture参数化依赖于内置fixture request 及其属性 param

    7. 内置的 fixture

    tmpdir & tmpdir_factory

    用于临时文件和目录管理,默认会在测试结束时删除。

    tmpdir 只有 function 作用域,只能在函数内使用。

    使用 tmpdir.mkdir() 创建目临时录,tmpdir.join() 创建临时文件(或者使用创建的目录)。

    def test_tmpdir(tmpdir):
        a_dir = tmpdir.mkdir('mytmpdir')
        a_file = a_dir.join('tmpfile.txt')
    
        a_file.write('hello, pytest!')
    
        assert a_file.read() == 'hello, pytest!'
    

    注解

    tmpdir_factory 可以在所有作用域使用,包括 function, class, module, session

    import pytest
    
    @pytest.fixture(scope='module')
    def my_tmpdir_factory(tmpdir_factory):
        a_dir = tmpdir_factory.mktemp('mytmpdir')
        a_file = a_dir.join('tmpfile.txt')
    
        a_file.write('hello, pytest!')
    
        return a_file
    

    pytestconfig

    使用 pytestconfig,可以很方便的读取命令行参数和配置文件。

    下面示例演示命令行参数解析:首先在 conftest.py 中使用函数 pytest_addoption (特定的 hook function ):

    # conftest.py
    
    def pytest_addoption(parser):
        parser.addoption('--host', action='store',
                         help='host of db')
        parser.addoption('--port', action='store', default='8888',
                         help='port of db')
    

    然后就可以在测试函数中通过 pytestconfig 获取命令行参数:

    # test_config.py
    
    def test_option1(pytestconfig):
        print('host: %s' % pytestconfig.getoption('host'))
        print('port: %s' % pytestconfig.getoption('port'))
    

    注解

    pytestconfig 其实是 request.config 的快捷方式,所以也可以自定义fixture实现命令行参数读取。

    # conftest.py
    
    def pytest_addoption(parser):
        parser.addoption('--host', action='store',
                         help='host of db')
        parser.addoption('--port', action='store', default='8888',
                         help='port of db')
    
    
    @pytest.fixture
    def config(request):
        return request.config
    
    
    # test_config.py
    
    def test_option2(config):
        print('host: %s' % config.getoption('host'))
        print('port: %s' % config.getoption('port'))
    

    执行结果:

    $ pytest -s --host=localhost tests/fixture/test_config.py::test_option2
    ============================= test session starts =============================
    platform win32 -- Python 3.6.4, pytest-3.6.1, py-1.5.2, pluggy-0.6.0
    rootdir: F:self-repolearning-pytest, inifile:
    collected 1 item
    
    testsfixture	est_config.py host: localhost
    port: 8888
    .
    
    ========================== 1 passed in 0.06 seconds ===========================
    

    capsys

    capsys 用于捕获 stdoutstderr 的内容,并临时关闭系统输出。

    # test_capsys.py
    
    def ping(output):
        print('Pong...', file=output)
    
    
    def test_stdout(capsys):
        ping(sys.stdout)
        out, err = capsys.readouterr()
        assert out == 'Pong...
    '
        assert err == ''
    
    
    def test_stderr(capsys):
        ping(sys.stderr)
        out, err = capsys.readouterr()
        assert out == ''
        assert err == 'Pong...
    '
    

    monkeypatch

    monkeypath 用于运行时动态修改类或模块。

    Pytest 内置 monkeypatch 提供的函数有:

    • setattr(target, name, value, raising=True),设置属性;
    • delattr(target, name, raising=True),删除属性;
    • setitem(dic, name, value),字典添加元素;
    • delitem(dic, name, raising=True),字典删除元素;
    • setenv(name, value, prepend=None),设置环境变量;
    • delenv(name, raising=True),删除环境变量;
    • syspath_prepend(path),添加系统路径;
    • chdir(path),切换目录。

    其中 raising 用于通知 pytest 在元素不存在时是否抛出异常;prepend 如果设置,环境变量将变为 value+prepend+<old value>

    这种在运行时控制程序的功能就需要 monkeypatch 来实现,下面在测试过程中修改了环境变量:

    # test_monkeypatch.py
    
    def test_config_monkeypatch(tmpdir, monkeypatch):
        monkeypatch.setenv('HOME', tmpdir.mkdir('home'))
    
        dump_config(config)
        path = os.path.expanduser('~/.conf.json')
        expected = json.load(open(path, 'r', encoding='utf-8'))
        assert expected == config
    

    现在测试会来临时目录中执行,但环境变量可能对系统有依赖,所以更好的解决方法能自己控制路径中 ~ 的替换,这次通过改变 os.path.expanduser 的行为来实现:

    recwarn

    recwarn 用于捕获程序中 warnings 产生的警告。

    # test_recwarn.py
    
    def warn():
        warnings.warn('Deprecated function', DeprecationWarning)
    
    
    def test_warn(recwarn):
        warn()
        assert len(recwarn) == 1
        w = recwarn.pop()
        assert w.category == DeprecationWarning
    

    此外,pytest 可以使用 pytest.warns() 捕获警告:

    def test_warn2():
        with pytest.warns(None) as warnings:
            warn()
    
        assert len(warnings) == 1
        w = warnings.pop()
        assert w.category == DeprecationWarning
    

    捕获stdout/stderr输出

    在测试运行期间,所有的 stdout / stderr 都会被捕获(不会输出到屏幕)。如果测试失败,则通常会显示相应的捕获输出以及失败。(此行为可以通过 --show-capture 命令行选项)。此外, stdin 设置为“空”对象,该对象在尝试读取时将失败,因为在运行自动测试时很少需要等待交互式输入。(执行期间不会打印任何输出,只有停止后才能一次性打印出来)

    设置捕获/禁用捕获

    有三种方法可以获取捕获:

    • fd (文件描述符) 级别的捕获(默认):所有到文件描述符1和2的内容都被捕获
    • sys 级别:只捕获写入到python文件 sys.stdoutsys.stderr 的才会被捕获. 不会捕获文件描述符的写入。
    • tee-sys 捕获: Python写入 sys.stdoutsys.stderr 将被捕获,但是写入也将传递到 sys.stdoutsys.stderr . 这允许输出被“实时打印”并捕获以供插件使用

    您可以从命令行设置捕获:

    pytest -s                  # disable all capturing
    pytest --capture=sys       # replace sys.stdout/stderr with in-mem files
    pytest --capture=fd        # also point filedescriptors 1 and 2 to temp file
    pytest --capture=tee-sys   # combines 'sys' and '-s', capturing sys.stdout/stderr
                               # and passing it along to the actual sys.stdout/stderr
    pytest --capture=no        # disable all capture,can show info in real time.
    

    命令行

    修改python回溯打印

    修改回溯打印的示例:

    pytest --showlocals # show local variables in tracebacks
    pytest -l           # show local variables (shortcut)
    
    pytest --tb=auto    # (default) 'long' tracebacks for the first and last
                         # entry, but 'short' style for the other entries
    pytest --tb=long    # exhaustive, informative traceback formatting
    pytest --tb=short   # shorter traceback format
    pytest --tb=line    # only one line per failure
    pytest --tb=native  # Python standard library formatting
    pytest --tb=no      # no traceback at all
    

    详细总结报告

    这个 -r 标记可以过滤 short test summary info 部分中展示的信息,

    它默认为 fE 列出失败和错误。

    以下是可用字符的完整列表:

    • f -失败
    • E -误差
    • s 跳过
    • x -失败
    • X -XPASS
    • p 通过
    • P -通过输出

    用于(取消)选择组的特殊字符:

    • a - all except pP
    • A -所有
    • N -无,这不能用来显示任何内容(因为 fE 是默认设置)
    ========================= short test summary info ==================
    SKIPPED [1] 222222.py:23: skipping this test    # 上面的选项作用于这一块,这里用了 -rs 参数,过滤出 skip 的测试
    ================ 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.27s =============
    

    分析测试执行持续时间

    要获取长度超过1.0秒的最慢10个测试持续时间的列表:

    pytest --durations=10 --durations-min=1.0
    

    默认情况下,pytest不会显示太小的测试持续时间(<0.005s),除非 -vv 在命令行上传递。

    查看缓存

    $ pytest --cache-show
    

    清除缓存

    pytest --cache-clear
    

    缓存:交叉测试

    只运行上次执行时失败的测试

    这个插件提供了两个命令行参数来重新执行上次运行失败的测试:

    • --lf, --last-failed : 只执行上次失败的测试
    • --ff, --failed-first :先执行上次失败的测试,然后执行其余的测试。

    如果清理缓存,--cache-clear 选项可以在执行测试前清除缓存。

    上次执行没有失败的测试

    当上次执行时,没有测试失败,或者也没有 lastfailed 数据,pytest 可以配置:要么执行所有测试,要么一个也不执行。

    pytest --last-failed --last-failed-no-failures all    # run all tests (default behavior)
    pytest --last-failed --last-failed-no-failures none   # run no tests and exit
    

    内置 fixture

    capfd()

    捕获文件描述符 1 和 2 到文件

    通过 capfd.readouterr() 捕获输出,返回一个 (out, err) . outerr 都是 text 对象。

    例如:

    import pytest
    import os
    
    def test_system_echo(capfd):
        os.system('echo hello')
        captured = capfd.readouterr()
        assert captured.out == "hello
    "
    

    capsys()

    捕获写入到 sys.stdoutsys.stderr 的文本

    通过 capsys.readouterr() 捕获, 返回一个 (out, err) 命名元组. outerr 都是 text 对象.

    例如:

    def test_output(capsys):
        print("hello")
        captured = capsys.readouterr()
        assert captured.out == "hello
    "
    

    插件

    pytest-rerunfailures

    此插件可以重新运行失败的测试用例。

    安装:

    pip install pytest-rerunfailures
    

    使用:

     pytest --reruns 2 --reruns-delay 5 test_sample.py # 对执行失败的用例重新运行最多2次,每次间隔5秒钟
    

    或者只针对某个测试用例:

    @pytest.mark.flaky(reruns=10, reruns_delay=1)
    def test_foo1():
        x = random.randint(1,5)
        assert x==2
    

    pytest-ordering

    对测试用例进行排序,order序号越小,越先执行。

    安装:

    pip install pytest-ordering
    

    使用:

    import pytest
    
    
    @pytest.mark.run(order=2)   # order序号小的先执行,也就是先执行 foo1,后 foo2
    def test_foo2():
        assert 1==1
        
    @pytest.mark.run(order=1)
    def test_foo1():
        assert 2==2
    
    pytest.main(['-sv','222222.py'])
    

    pytest-timeout

    设置用例超时时间,超过时间,fail

    安装:

    pip install pytest-timeout
    

    使用:

    @pytest.mark.timeout(5)
    def test_time():  # 当前用例限定5s超时
       ...
    

    或者:

    pytest --timeout=5 test_sample.py  # 对所有用例都限定超时
    

    pytest-assume

    针对一个测试用例中多次断言,可以忽视断言的成败,执行完所有断言

    安装:

    pip install pytest-assume
    

    不用assume:

    def test_foo1():
        assert 1==2 # 执行到这里失败,下面的断言不会被执行
        assert 3==2
    

    使用:

    def test_foo1():
        pytest.assume(1==2)
        pytest.assume(3==2) # 尽管上面的断言失败了,但是这个也会继续执行
    

    pytest-html

    生成html报告

    安装:

    pip install pytest-html
    

    使用:

    pytest --html=./report.html test_sample.py  		# 生成一个叫 report.html 的报告(css文件单独放置)
    pytest --html=./report.html --self-contained-html 	# 将css和html合并
    

    pytest-sugar

    显示执行进度

    安装:

    pip install pytest-sugar
    

    pytest-xdist

    使用多核cpu并行执行用例

    安装:

    pip install pytest-xdist
    

    使用:

    pytest -n 2 test_sample.py # 使用2个线程并行
    

    pytest-cover

    测试覆盖率

    安装:

    pip install pytest-cover
    

    allure-pytest

    allure插件,根据allure生成美观的报告

    1. 安装插件:
    pip install allure-pytest
    
    1. 下载安装allure,解压后将 bin 文件夹路径添加到环境变量:

    https://dl.bintray.com/qameta/generic/io/qameta/allure/allure/2.7.0/allure-2.7.0.zip

    执行命令,查看是否配置成功(不行的话,可能需要java运行环境支持):

    C:Userswztshine>allure --version
    2.7.0
    
    1. 编写测试脚本:
    import allure
    import pytest
    
    
    @allure.feature('test_module_01')
    def test_case_01():
        """
        用例描述:Test case 01
        """
        assert 0
        
    @allure.feature('test_module_02')
    def test_case_02():
        """
        用例描述:Test case 02
        """
        assert 0 == 0
    
    1. 执行脚本:
    pytest -s test_2.py --alluredir ./report # 执行 test_2.py中的测试,设置alluredir目录为:./report
    
    1. 生成 html 报告,并查看
    allure generate ./report -o ./html --clean  # 针对上一步的 report 目录,生成一个 html 目录,里面存放了报告
    allure open ./html                          # 打开报告,会自动调用开启一个web服务打开网页
    allure report clean							# 清理报告数据
    

    steps

    Allure 报告首要并且可能最重要的是,它允许获得每个测试调用的非常详细的分步表示。这是通过 @allure.step 装饰器来实现的,它将注释的方法或函数的调用与提供的参数都添加到报表中。

    @step 可以存储在测试之外,并只在需要时导入。Step 方法可以有任意深嵌套的结构。

    例如:

    import allure
    import pytest
    
    
    @allure.step
    def test_step1():
        """
        用例描述:Test step 1
        """
        test_step2()
        
    @allure.step
    def test_step2():
        """
        用例描述:Test step 2
        """
        assert 0 == 1
    
    def test_step():
        test_step1()
        test_step2()
        
    

    步骤可以有一个描述行,该描述行支持传递的位置参数和关键字参数的占位符,关键字参数的默认参数也将被捕获。

    import allure
    
    @allure.step('Step with placeholders in the title, positional: "{0}", keyword: "{key}"')
    def step_with_title_placeholders(arg1, key=None):
        pass
    
    
    def test_steps_with_placeholders():
        step_with_title_placeholders(1, key='something')
        step_with_title_placeholders(2)
        step_with_title_placeholders(3, 'anything')
    

    Attachments

    报告可以显示许多不同类型的附件,这些附件可以作为测试、步骤或 fixture 结果的补充。附件可以通过 allure.attach 创建(body, name, attachment_type, extension):

    1. body - 要写入文件的原始内容,可以是文字,图片等。
    2. name - 文件名的字符串
    3. attachment_type - 一个 allure.attachment_type 值,附件类型
    4. extension - 被用作文件的扩展,可以忽略

    或者 allure.attach.filesource, name, attachment_type, extension):

    1. source - 包含文件路径的字符串

    (其他参数一样)

    import allure
    import pytest
    
    
    def test_multiple_attachments():
        allure.attach.file(r'C:UserswztshineDesktopwallhaven-73dyxo.jpg', attachment_type=allure.attachment_type.PNG)
        allure.attach('<head></head><body> a page </body>', 'Attach with HTML type', allure.attachment_type.HTML)
    

    附件显示在它们所属的测试实体的上下文中。HTML 类型的附件将呈现并显示在报告页面上,这是一种方便的方法,可以为您自己的测试结果表示提供一些定制化。

    Descriptions

    您可以添加测试的详细描述,以便为报表阅读提供所需的上下文。这可以通过几种方式实现:您可以添加一个 @allure.description 装饰器来提供一个描述字符串,或者您可以使用 @allure.description_html 来提供一些 HTML,以便在测试用例的 “description” 部分中呈现。

    import allure
    import pytest
    
    
    @allure.description("this is description")  # 描述内容
    def test_multiple_attachments():
        allure.attach.file(r'C:UserswztshineDesktopwallhaven-73dyxo.jpg', attachment_type=allure.attachment_type.PNG)
        allure.attach('<head></head><body> a page </body>', 'Attach with HTML type', allure.attachment_type.HTML)
    

    也可以不写描述内容,直接使用函数的描述

    import allure
    import pytest
    
    
    @allure.description     # 不写描述内容,直接使用函数的描述
    def test_des():
        """ this is des"""   # 用这句话当描述
        assert 0==0
    

    描述也可以使用 allure.dynamic.description 从测试体的内部动态更新。

    import allure
    
    @allure.description("""
    This description will be replaced at the end of the test.
    """)
    def test_dynamic_description():
        assert 42 == int(6 * 7)
        allure.dynamic.description('A final description.')
    

    Titles

    特殊的 @allure.title 装饰器可以使测试标题更具可读性。标题支持参数占位符并支持动态替换。

    import allure
    import pytest
    
    
    @allure.title("This test has a custom title")
    def test_with_a_title():
        assert 2 + 2 == 4
    

    要想将报告与缺陷跟踪或测试管理集成,可以使用:@allure.link, @allure.issue@allure.testcase

    import allure
    
    TEST_CASE_LINK = 'https://github.com/qameta/allure-integrations/issues/8#issuecomment-268313637'
    
    
    @allure.link('https://www.youtube.com/watch?v=4YYzUTYZRMU') # 直接显示为网址
    def test_with_link():
        pass
    
    
    @allure.link('https://www.youtube.com/watch?v=Su5p2TqZxKU', name='Click me') # 链接文本
    def test_with_named_link():
        pass
    
    
    @allure.issue('140', 'Pytest-flaky test') # 会打开192.16.../140,也就是当前web服务的某个issueid的网址。
    def test_with_issue_link():
        pass
    
    
    @allure.testcase(TEST_CASE_LINK, 'Test case title') # 链接文本
    def test_with_testcase_link():
        pass
    

    BDD markers

    BDD,Behavior-driven development

    类似于pytest的mark标记,可以通过标记进行测试用例的过滤,只执行某些标记。

    有两个装饰器可以根据项目的特性/故事分解来标记测试: @allure.feature@allure.story背景资料请参阅Wikipedia上的BDD文章)。为了表明某个特征或故事属于某个史诗,名字可以使用 epic_ 前缀开头。

    您可以使用以下命令行选项来指定不同的测试集来执行传递一列逗号分隔的值:

    • --allure-epics
    • --allure-features
    • --allure-stories

    例子:

    import allure
    
    def test_without_any_annotations_that_wont_be_executed():
        pass
    
    @allure.story('epic_1')
    def test_with_epic_1():
        pass
    
    @allure.story('story_1')
    def test_with_story_1():
        pass
    
    @allure.story('story_2')
    def test_with_story_2():
        pass
    
    @allure.feature('feature_2')
    @allure.story('story_2')
    def test_with_story_2_and_feature_2():
        pass
    
    $ pytest tests.py --allure-stories story_1,story_2
    
    collected 5 items
    
    tests.py ...                                                                    [100%]
    
    ============================== 3 passed in 0.01 seconds ==============================
    $ pytest tests.py --allure-features feature2 --allure-stories story2
    
    collected 5 items
    
    tests.py ...                                                                     [100%]
    
    =============================== 2 passed in 0.01 seconds ==============================
    

    Severity markers

    要根据测试的严重程度对测试进行评分,可以使用 @allure.severity 装饰器。它需要 allure.severity_level 枚举值作为参数。

    tests.py

    import allure
    
    
    def test_with_no_severity_label():
        pass
    
    
    @allure.severity(allure.severity_level.TRIVIAL)
    def test_with_trivial_severity():
        pass
    
    
    @allure.severity(allure.severity_level.NORMAL)
    def test_with_normal_severity():
        pass
    
    
    @allure.severity(allure.severity_level.NORMAL)
    class TestClassWithNormalSeverity(object):
    
        def test_inside_the_normal_severity_test_class(self):
            pass
    
        @allure.severity(allure.severity_level.CRITICAL)
        def test_inside_the_normal_severity_test_class_with_overriding_critical_severity(self):
            pass
    

    Severity 装饰器可以应用于函数、方法或整个类。

    通过使用带有逗号分隔的严重性级别列表的 --allure-severities 命令行选项,只有具有相应严重性的测试才会运行。

    $ pytest tests.py --allure-severities normal,critical
    
    collected 5 items
    
    bdd_annotations_demo/test_severity_labels.py ...                                [100%]
    
    ================================ 3 passed in 0.01 seconds ============================
    
  • 相关阅读:
    51nod1370 排列与操作
    2019-11-20-Github-给仓库上传-NuGet-库
    2019-11-20-Github-给仓库上传-NuGet-库
    2019-6-5-WPF-隐藏系统窗口菜单
    2019-6-5-WPF-隐藏系统窗口菜单
    2019-8-31-AutoHotKey-用打码的快捷键
    2019-8-31-AutoHotKey-用打码的快捷键
    2019-4-10-win10-uwp-自定义标记扩展
    2019-4-10-win10-uwp-自定义标记扩展
    2018-9-3-C#-const-和-readonly-有什么区别
  • 原文地址:https://www.cnblogs.com/wztshine/p/14645440.html
Copyright © 2011-2022 走看看