Python单元测试框架——pytest(一)


Pytest介绍

Pytest是一款非常成熟的全功能的python单元测试框架,支持自动检索用例、参数化、跳过、标记预期等功能,且可以通过安装插件进行功能的扩展,与python自带的unittest单元测试框架类似,但比unittest框架使用起来更简洁,功能更强大。

Pytest是一款命令行工具,可以自动找到测试用例执行,并且汇报测试结果。它有丰富的基础库,可以大幅提高用户编写测试用例的效率。它具备可扩展性,用户可以自己编写插件,或者安装第三方提供的插件(Pytest支持1170种以上的插件)。Pytest可以直接测试各类Python程序,也可以很容易地与其他工具集成到一起使用,比如持续集成、自动化测试等。同时Pytest是一个非常成熟的单元测试框架,灵活、简单。可以通过结合自动化测试框架RequestsSeleniumAppium来完成不同类型的自动化测试。

利用pytest单元测试框架的具体使用步骤:

  • 用例设计:按照pytest测试用例设计规则进行用例设计;
  • 测试发现:从多个文件里查找到符合条件的测试用例;
  • 测试执行:按照一定的顺序和规则去执行,生成结果;
  • 测试判断:通过断言判断预期结果和实际结果是否一致;
  • 测试报告:统计测试进度、耗时、通过率等,生成测试报告;

Pytest安装

Pytest的官方文档:https://docs.pytest.org/en/7.2.x/

在已经安装了python3.6以上版本的基础上,通过下面2种方式都可以进行安装:

  1. 通过命令行

    pip install pytest
  2. 通过Pycharm开发工具

验证安装

pytest --version
pytest 7.2.0

编写用例

pytest在执行用例时,会自动递归遍历执行目录下的所有目录,根据pytest默认的用例收集规则,自动装载用例。

用例规范

  • 用例文件,所有文件名为test_开头或者_test结尾的文件会被识别为用例文件;
  • 用例类,测试文件中每个以Test开头的类就是一个测试用例类;
  • 用例方法/函数,每个以test_开头的方法/函数就是一条测试用例;

注:测试类中不可以添加__init__构造函数

上面是设计pytest测试用例的规则,也是执行用例的时默认的收集用例规则,可以在配置文件中进行设定修改。

函数形式编写用例

用例函数名以test_开头

def test_demo():
    assert 3 == 3

类形式编写用例

测试类命名以Test开头,用例方法以test_开头

class TestDome:

    def test_demo1(self):
        assert 3 == 3

    def test_demo(self):
        assert 10 == 20

执行测试用例

执行用例有两种方式,命令行及IDE执行(这里的IDE指的是Pycharm)。绝大多数执行测试用例都是用命令行方式,包括集成到CI/CD中都是以命令行方式调用,而IDE形式只用于调试。

测试用例的执行顺序:

  • Pycharm中通过运行方式执行的用例,按照pytest默认的规则:按ASCII的顺序进行执行;
  • 通过命令行方式执行的用例,是按照用例编写的顺序,即从上到下的顺序进行执行;

测试用例准备

根据pytest的规范创建3个pytest格式的测试用例文件,如下:

test_01_demo.py

import pytest


# 函数func
def func(x):
    return x + 1


# 测试函数test_a
def test_a():
    print("____test-a____")
    # 断言失败
    assert func(3) == 5


# 测试函数test_b
def test_b():
    print("____test-b____")
    # 断言成功
    assert func(4) == 5


if __name__ == '__main__':
    pytest.main(["test_01_demo.py"])

test_02_demo.py

import pytest


class TestFunc:

    def test_a(self):
        print("test_a")

    def test_b(self):
        print("test_b")


if __name__ == '__main__':
    pytest.main(["test_02_demo.py"])

test_03_demo.py

import pytest


class TestClass:

    def test_a(self):
        print("test_a")

    def test_b(self):
        print("test_b")


if __name__ == '__main__':
    pytest.main(["test_03_demo.py"])

在Pycharm中执行用例

  1. 需要将Pycharm的默认单元测试框架设置为pytest。在Preferences-Tools-Python Integrated Tools-Testingdefault test runner设置为pytest,保存。

  2. 在某个用例处执行用例,即执行当前光标所在位置的用例

    Launching pytest with arguments test_01_demo.py::test_b --no-header --no-summary -q in /Users/laobai/workspaces/testdemo01/pytest_demo
    
    ============================= test session starts ==============================
    collecting ... collected 1 item
    
    test_01_demo.py::test_b PASSED                                           [100%]____test-b____
    
    
    ============================== 1 passed in 0.01s ===============================
    
    Process finished with exit code 0
  3. 在main函数或空白处执行用例,执行当前模块中的全部用例

    Launching pytest with arguments /Users/laobai/workspaces/testdemo01/pytest_demo/test_01_demo.py --no-header --no-summary -q in /Users/laobai/workspaces/testdemo01/pytest_demo
    
    ============================= test session starts ==============================
    collecting ... collected 2 items
    
    test_01_demo.py::test_a FAILED                                           [ 50%]____test-a____
    
    test_01_demo.py:13 (test_a)
    4 != 5
    
    Expected :5
    Actual   :4
    <Click to see difference>
    
    def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    
    
    test_01_demo.py::test_b PASSED                                           [100%]____test-b____
    
    
    ========================= 1 failed, 1 passed in 0.05s ==========================
    
    Process finished with exit code 1

命令行模式执行用例

确认用例文件的位置:

$ pwd
/Users/laobai/workspaces/testdemo01/pytest_demo

$ ll
total 24
drwxr-xr-x  7 laobai  staff  224 Dec  7 11:53 ./
drwxr-xr-x  7 laobai  staff  224 Dec  7 10:33 ../
drwxr-xr-x  6 laobai  staff  192 Dec  7 10:38 .pytest_cache/
drwxr-xr-x  5 laobai  staff  160 Dec  7 11:53 __pycache__/
-rw-r--r--  1 laobai  staff  430 Dec  7 11:07 test_01_demo.py
-rw-r--r--  1 laobai  staff  281 Dec  7 11:07 test_02_demo.py
-rw-r--r--  1 laobai  staff  282 Dec  7 11:07 test_03_demo.py
  1. 在用例所在目录,直接执行pytest命令,即可执行当前目录下的所有测试用例

    $ pytest
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.05s ===================================================================

    简单描述一下执行结果:

    collected 6 items 6个测试用例

    test_01_demo.py F. F是测试失败,.是成功

    short test summary info 测试结果摘要,列出出错的文件及方法,统计失败数和成功数及耗费时间

  2. pytest增加-s参数,输出print()函数的信息

    $ pytest -s
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py ____test-a____
    F____test-b____
    .
    test_02_demo.py test_a
    .test_b
    .
    test_03_demo.py test_a
    .test_b
    .
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
  3. pytest增加-v参数,输出用例执行的详细过程

    $ pytest -v
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py::test_a FAILED                                                                                                                              [ 16%]
    test_01_demo.py::test_b PASSED                                                                                                                              [ 33%]
    test_02_demo.py::TestFunc::test_a PASSED                                                                                                                    [ 50%]
    test_02_demo.py::TestFunc::test_b PASSED                                                                                                                    [ 66%]
    test_03_demo.py::TestClass::test_a PASSED                                                                                                                   [ 83%]
    test_03_demo.py::TestClass::test_b PASSED                                                                                                                   [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
  4. pytest增加-v参数,精简输出用例执行的过程

    $ pytest -q
    F.....                                                                                                                                                      [100%]
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    1 failed, 5 passed in 0.04s
  5. 指定要执行的目录下的所有用例

    $ pytest pytest_demo/
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01
    collected 6 items                                                                                                                                                 
    
    pytest_demo/test_01_demo.py F.                                                                                                                              [ 33%]
    pytest_demo/test_02_demo.py ..                                                                                                                              [ 66%]
    pytest_demo/test_03_demo.py ..                                                                                                                              [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    pytest_demo/test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED pytest_demo/test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
  6. 指定要执行的某个文件(模块)

    $ pytest test_02_demo.py 
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 2 items                                                                                                                                                 
    
    test_02_demo.py ..                                                                                                                                          [100%]
    
    ======================================================================== 2 passed in 0.01s ========================================================================
  7. 指定要执行某个文件中的某个类

    $ pytest test_02_demo.py::TestFunc 
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 2 items                                                                                                                                                 
    
    test_02_demo.py ..                                                                                                                                          [100%]
    
    ======================================================================== 2 passed in 0.01s ========================================================================
  8. 指定要执行某个文件某个类的某个方法

    $ pytest test_02_demo.py::TestFunc::test_b
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 1 item                                                                                                                                                  
    
    test_02_demo.py .                                                                                                                                           [100%]
    
    ======================================================================== 1 passed in 0.01s ========================================================================
  9. 通过参数-k过滤用例,按关键字查找执行,在文件名、类名以及方法名中查找关键字k

    $ pytest -k "test_b" -v
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items / 3 deselected / 3 selected                                                                                                                     
    
    test_01_demo.py::test_b PASSED                                                                                                                              [ 33%]
    test_02_demo.py::TestFunc::test_b PASSED                                                                                                                    [ 66%]
    test_03_demo.py::TestClass::test_b PASSED                                                                                                                   [100%]
    
    ================================================================= 3 passed, 3 deselected in 0.01s =================================================================
    $ pytest -k "not test_a" -v
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items / 3 deselected / 3 selected                                                                                                                     
    
    test_01_demo.py::test_b PASSED                                                                                                                              [ 33%]
    test_02_demo.py::TestFunc::test_b PASSED                                                                                                                    [ 66%]
    test_03_demo.py::TestClass::test_b PASSED                                                                                                                   [100%]
    
    ================================================================= 3 passed, 3 deselected in 0.01s =================================================================
    $ pytest -k "test_a or test_b" -v
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py::test_a FAILED                                                                                                                              [ 16%]
    test_01_demo.py::test_b PASSED                                                                                                                              [ 33%]
    test_02_demo.py::TestFunc::test_a PASSED                                                                                                                    [ 50%]
    test_02_demo.py::TestFunc::test_b PASSED                                                                                                                    [ 66%]
    test_03_demo.py::TestClass::test_b PASSED                                                                                                                   [ 83%]
    test_03_demo.py::TestClass::test_a PASSED                                                                                                                   [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
  10. 执行测试时,增加-x参数,-x:用例运行失败则立即停止执行

    $ pytest -x
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ======================================================================== 1 failed in 0.04s ========================================================================
  11. 执行用例时增加--maxfail=num参数,--maxfail:是最大失败次数,当批量执行用例时,失败次数大于等于指定的数值时,执行会立即停止。

    $ pytest --maxfail=1
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ======================================================================== 1 failed in 0.04s ========================================================================
    $ pytest --maxfail=2
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
  12. 执行用例时增加--lf参数,--lf(–last-failed):执行上次失败的测试用例

    $ pytest --lf
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 1 item                                                                                                                                                  
    run-last-failure: rerun previous 1 failure (skipped 2 files)
    
    test_01_demo.py F                                                                                                                                           [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    ======================================================================== 1 failed in 0.04s ========================================================================
  13. 执行用例时增加--ff参数,--ff(–failed-first):先执行上次失败的测试用例再运行其他用例

    $ pytest -v test_01_demo.py --ff
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
    collected 3 items                                                                                                                                                 
    run-last-failure: rerun previous 1 failure first
    
    test_01_demo.py::test_a FAILED                                                                                                                              [ 33%]
    test_01_demo.py::test_0 PASSED                                                                                                                              [ 66%]
    test_01_demo.py::test_b PASSED                                                                                                                              [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            # time.sleep(2)
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:25: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 2 passed in 0.05s ===================================================================
  14. 执行用例时增加--tb参数,--tb:输出信息的显示模式(auto/long/short/line/native/no)

    --tb=no 不显示用例失败的错误详情

    $ pytest --tb=no
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.02s ===================================================================

    --tb=short 只显示assert断言的错误信息

    $ pytest --tb=short
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    test_01_demo.py:17: in test_a
        assert func(3) == 5
    E   assert 4 == 5
    E    +  where 4 = func(3)
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.04s ===================================================================

    --tb=line 显示用例失败的代码行数

    $ pytest --tb=line
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    /Users/laobai/workspaces/testdemo01/pytest_demo/test_01_demo.py:17: assert 4 == 5
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.02s ===================================================================
  15. 执行用例时增加--collect-only参数,--collect-only:仅收集可执行的用例,但不执行

    $ pytest --collect-only
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    <Module test_01_demo.py>
      <Function test_a>
      <Function test_b>
    <Module test_02_demo.py>
      <Class TestFunc>
        <Function test_a>
        <Function test_b>
    <Module test_03_demo.py>
      <Class TestClass>
        <Function test_b>
        <Function test_a>
    
    =================================================================== 6 tests collected in 0.01s ====================================================================
  16. 执行用例时增加-p no:warnings参数,-p no:warnings:过滤警告信息

    $ pytest -p no:warnings
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.05s ===================================================================
  17. 执行用例时增加--durations=num -vv参数,num=0时则是按执行时间倒序排列所有执行用例,num为具体数值n时显示耗时最长的n个用例。若用例执行速度极快,会在统计是显示成0,-vv 显示执行时间为0的用例

    $ pytest --durations=0 -vv
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py::test_a FAILED                                                                                                                              [ 16%]
    test_01_demo.py::test_b PASSED                                                                                                                              [ 33%]
    test_02_demo.py::TestFunc::test_a PASSED                                                                                                                    [ 50%]
    test_02_demo.py::TestFunc::test_b PASSED                                                                                                                    [ 66%]
    test_03_demo.py::TestClass::test_b PASSED                                                                                                                   [ 83%]
    test_03_demo.py::TestClass::test_a PASSED                                                                                                                   [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ======================================================================== slowest durations ========================================================================
    0.00s call     test_01_demo.py::test_a
    0.00s setup    test_01_demo.py::test_a
    0.00s call     test_01_demo.py::test_b
    0.00s teardown test_01_demo.py::test_a
    0.00s setup    test_02_demo.py::TestFunc::test_a
    0.00s call     test_02_demo.py::TestFunc::test_a
    0.00s teardown test_01_demo.py::test_b
    0.00s call     test_02_demo.py::TestFunc::test_b
    0.00s call     test_03_demo.py::TestClass::test_b
    0.00s call     test_03_demo.py::TestClass::test_a
    0.00s setup    test_01_demo.py::test_b
    0.00s setup    test_03_demo.py::TestClass::test_b
    0.00s setup    test_02_demo.py::TestFunc::test_b
    0.00s setup    test_03_demo.py::TestClass::test_a
    0.00s teardown test_03_demo.py::TestClass::test_b
    0.00s teardown test_02_demo.py::TestFunc::test_a
    0.00s teardown test_03_demo.py::TestClass::test_a
    0.00s teardown test_02_demo.py::TestFunc::test_b
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.04s ===================================================================
    $ pytest --durations=3 -vv
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py::test_a FAILED                                                                                                                              [ 16%]
    test_01_demo.py::test_b PASSED                                                                                                                              [ 33%]
    test_02_demo.py::TestFunc::test_a PASSED                                                                                                                    [ 50%]
    test_02_demo.py::TestFunc::test_b PASSED                                                                                                                    [ 66%]
    test_03_demo.py::TestClass::test_b PASSED                                                                                                                   [ 83%]
    test_03_demo.py::TestClass::test_a PASSED                                                                                                                   [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ======================================================================= slowest 3 durations =======================================================================
    0.00s call     test_01_demo.py::test_a
    0.00s setup    test_01_demo.py::test_a
    0.00s call     test_02_demo.py::TestFunc::test_b
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.05s ===================================================================

Mark标记

在执行测试用例时,经常会遇到,要对某个功能的执行测试用例,对某个测试环境的执行测试用例,这就要求在书写设计用例时要做好分类,而pytest提供了mark功能,可以通过装饰器的形式给用例打标签,可以给一个用例打多个标签。打标签的对象,可以是测试用例,测试类以及模块文件。

mark标签

语法格式:@pytest.mark.标签名

执行用例时,增加-m参数,后面跟上标签名

新建一个测试用例文件,test_05_demo.py,共4个测试用例,标记webtest2条,iostest1条,androidtest1条。

import pytest


class TestClass:

    @pytest.mark.webtest
    def test_a(self):
        print("test_a")

    @pytest.mark.iostest
    def test_b(self):
        print("test_b")

    @pytest.mark.webtest
    def test_c(self):
        print("test_c")

    @pytest.mark.androidtest
    def test_d(self):
        print("test_d")

    @pytest.mark.iostest
    @pytest.mark.androidtest
    def test_e(self):
        print("test_e")


if __name__ == '__main__':
    pytest.main(["test_05_demo.py"])

按区分的标记执行用例

$ pytest -v -m webtest
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 15 items / 13 deselected / 2 selected                                                                                                                   

test_05_demo.py::TestClass::test_a PASSED                                                                                                                   [ 50%]
test_05_demo.py::TestClass::test_c PASSED                                                                                                                   [100%]

======================================================================== warnings summary =========================================================================
test_05_demo.py:10
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:10: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.webtest

test_05_demo.py:14
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.iostest

test_05_demo.py:18
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:18: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.webtest

test_05_demo.py:22
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:22: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.androidtest

test_05_demo.py:26
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:26: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.iostest

test_05_demo.py:27
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:27: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.androidtest

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================================================== 2 passed, 13 deselected, 6 warnings in 0.03s ===========================================================
$ pytest -v -m 'not webtest' test_05_demo.py 
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected                                                                                                                     

test_05_demo.py::TestClass::test_b PASSED                                                                                                                   [ 33%]
test_05_demo.py::TestClass::test_d PASSED                                                                                                                   [ 66%]
test_05_demo.py::TestClass::test_e PASSED                                                                                                                   [100%]

======================================================================== warnings summary =========================================================================
test_05_demo.py:10
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:10: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.webtest

test_05_demo.py:14
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.iostest

test_05_demo.py:18
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:18: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.webtest

test_05_demo.py:22
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:22: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.androidtest

test_05_demo.py:26
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:26: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.iostest

test_05_demo.py:27
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:27: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.androidtest

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================================================== 3 passed, 2 deselected, 6 warnings in 0.02s ===========================================================
$ pytest -v -m=iostest test_05_demo.py 
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 3 deselected / 2 selected                                                                                                                     

test_05_demo.py::TestClass::test_b PASSED                                                                                                                   [ 50%]
test_05_demo.py::TestClass::test_e PASSED                                                                                                                   [100%]

======================================================================== warnings summary =========================================================================
test_05_demo.py:10
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:10: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.webtest

test_05_demo.py:14
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:14: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.iostest

test_05_demo.py:18
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:18: PytestUnknownMarkWarning: Unknown pytest.mark.webtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.webtest

test_05_demo.py:22
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:22: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.androidtest

test_05_demo.py:26
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:26: PytestUnknownMarkWarning: Unknown pytest.mark.iostest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.iostest

test_05_demo.py:27
  /Users/laobai/workspaces/testdemo01/pytest_demo/test_05_demo.py:27: PytestUnknownMarkWarning: Unknown pytest.mark.androidtest - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    @pytest.mark.androidtest

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
=========================================================== 2 passed, 3 deselected, 6 warnings in 0.02s ===========================================================

上面执行用例时有大量的warnings信息,可以使用之前介绍的参数-p no:warnings过滤掉warnings信息

$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected                                                                                                                     

test_05_demo.py::TestClass::test_b PASSED                                                                                                                   [ 33%]
test_05_demo.py::TestClass::test_d PASSED                                                                                                                   [ 66%]
test_05_demo.py::TestClass::test_e PASSED                                                                                                                   [100%]

================================================================= 3 passed, 2 deselected in 0.02s =================================================================

注:命令中包含了较多参数,是可以配置到pytest.ini中的,这个会在后面的内容进行介绍。

当标签数量比较多,而且又会发生标签名改动的问题,可以将所有的标签抽象到一个py文件中进行统一管理。

上面的例子中,可以新建一个mark.py文件如下:

import pytest

webtest = pytest.mark.webtest
iostest = pytest.mark.iostest
android = pytest.mark.androidtest

这样测试用例文件就可以改写为如下:

from mark import *

class TestClass:

    @webtest
    def test_a(self):
        print("test_a")

    @iostest
    def test_b(self):
        print("test_b")

    @webtest
    def test_c(self):
        print("test_c")

    @androidtest
    def test_d(self):
        print("test_d")

    @iostest
    @androidtest
    def test_e(self):
        print("test_e")


if __name__ == '__main__':
    pytest.main(["test_05_demo.py"])

执行测试:

 $ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected                                                                                                                     

test_05_demo.py::TestClass::test_b PASSED                                                                                                                   [ 33%]
test_05_demo.py::TestClass::test_d PASSED                                                                                                                   [ 66%]
test_05_demo.py::TestClass::test_e PASSED                                                                                                                   [100%]

================================================================= 3 passed, 2 deselected in 0.03s =================================================================

通常情况下,打标签有3类:区分环境(测试、生产)、用例作者、特定用途(冒烟)

mark.skip用例跳过

在自动化测试过程中,经常会遇到功能阻塞、功能未实现、环境因素等导致的用例无法正常执行,此时可以通过跳过这些故障用例来完成测试,pytest提供了skip功能来实现。

@pytest.mark.skip()

代码片段

@iostest
@androidtest
@pytest.mark.skip()
def test_e(self):
    print("test_e")

测试执行:

$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected                                                                                                                     

test_05_demo.py::TestClass::test_b PASSED                                                                                                                   [ 33%]
test_05_demo.py::TestClass::test_d PASSED                                                                                                                   [ 66%]
test_05_demo.py::TestClass::test_e SKIPPED (unconditional skip)                                                                                             [100%]

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

@pytest.mark.skip(reason=’’)

代码片段

@iostest
@androidtest
@pytest.mark.skip(reason='功能尚未完成')
def test_e(self):
    print("test_e")

测试执行

$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected                                                                                                                     

test_05_demo.py::TestClass::test_b PASSED                                                                                                                   [ 33%]
test_05_demo.py::TestClass::test_d PASSED                                                                                                                   [ 66%]
test_05_demo.py::TestClass::test_e SKIPPED (功能尚未完成)                                                                                                   [100%]

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

@pytest.mark.skipif(判断条件,reason=’’)

判断条件满足,跳过执行此用例

代码片段

@iostest
@androidtest
@pytest.mark.skipif(2 == 2, reason='条件判断成功,跳过此用例')
def test_e(self):
    print("test_e")

测试执行

$ pytest -v -m='iostest or androidtest' test_05_demo.py -p no:warnings
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 5 items / 2 deselected / 3 selected                                                                                                                     

test_05_demo.py::TestClass::test_b PASSED                                                                                                                   [ 33%]
test_05_demo.py::TestClass::test_d PASSED                                                                                                                   [ 66%]
test_05_demo.py::TestClass::test_e SKIPPED (条件判断成功,跳过此用例)                                                                                       [100%]

=========================================================== 2 passed, 1 skipped, 2 deselected in 0.03s ============================================================

mark.flaky()

通过对用例增加@pytest.mark.flaky装饰器,来对仅用于此装饰器修饰的用例进行重试。有两个参数,reruns=n (n表示重试次数),reruns_delay=n(每次重跑的等待时间,n表示等待n秒)

新建test_08_demo.py

import pytest


# 函数func
def func(x):
    return x + 1


@pytest.mark.flaky(reruns=2, reruns_delay=2)
def test_a():
    print("____test-a____")
    # 断言失败
    assert func(3) == 5


def test_b():
    print("____test-b____")
    # 断言成功
    assert func(4) == 5


def test_c():
    print("____test-c____")
    # 断言成功
    assert func(1) == 5


if __name__ == '__main__':
    pytest.main(["test_08_demo.py"])

执行用例:

$ pytest -vs test_08_demo.py 
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 3 items                                                                                                                                                 

test_08_demo.py::test_a ____test-a____
RERUN
test_08_demo.py::test_a ____test-a____
RERUN
test_08_demo.py::test_a ____test-a____
FAILED
test_08_demo.py::test_b ____test-b____
PASSED
test_08_demo.py::test_c ____test-c____
FAILED

============================================================================ FAILURES =============================================================================
_____________________________________________________________________________ test_a ______________________________________________________________________________

    @pytest.mark.flaky(reruns=2,reruns_delay=2)
    def test_a():
        # time.sleep(2)
        print("____test-a____")
        # 断言失败
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_08_demo.py:20: AssertionError
_____________________________________________________________________________ test_c ______________________________________________________________________________

    def test_c():
        # time.sleep(2)
        print("____test-c____")
        # 断言成功
>       assert func(1) == 5
E       assert 2 == 5
E        +  where 2 = func(1)

test_08_demo.py:36: AssertionError
===================================================================== short test summary info =====================================================================
FAILED test_08_demo.py::test_a - assert 4 == 5
FAILED test_08_demo.py::test_c - assert 2 == 5
============================================================== 2 failed, 1 passed, 2 rerun in 4.07s ===============================================================

第三方插件

pytest-xdist

开启多个worker进程,同时执行多个测试用例,达到并发运行的效果,大大提升构建效率。

  1. 安装

    pip install pytest-xdist
  2. 模拟并发执行,在每个函数中都增加time.sleep(2),每个函数都sleep2秒(否则不到1ms就都执行完了),体现出多线程执行的作用。

  3. 示例演示

    import pytest
    import time
    
    # 函数func
    def func(x):
        return x + 1
    
    
    # 测试函数test_a
    def test_a():
        time.sleep(2)
        print("____test-a____")
        # 断言失败
        assert func(3) == 5
    
    
    # 测试函数test_b
    def test_b():
        time.sleep(2)
        print("____test-b____")
        # 断言成功
        assert func(4) == 5
    import pytest
    import time
    
    class TestFunc:
    
        def test_a(self):
            time.sleep(2)
            print("test_a")
    
        def test_b(self):
            time.sleep(2)
            print("test_b")
    import pytest
    import time
    
    class TestClass:
    
    
    
        def test_b(self):
            time.sleep(2)
            print("test_b")
    
        def test_a(self):
            time.sleep(2)
            print("test_a")

    首先不添加参数执行上面3个用例

    $ pytest
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    plugins: rerunfailures-10.3, html-3.2.0, metadata-2.0.4
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            time.sleep(2)
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:18: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    ================================================================== 1 failed, 5 passed in 12.11s ===================================================================

    可以看到上面3个文件6个用例执行的总耗时为12.11s

    增加-n参数再次执行,-n后面跟着一个数字,是启动几个线程,这里使用auto,表示根据当前cpu的核心数由pytest自动启动运行。

    $ pytest -n auto
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, metadata-2.0.4
    gw0 [6] / gw1 [6] / gw2 [6] / gw3 [6] / gw4 [6] / gw5 [6] / gw6 [6] / gw7 [6]
    .....F                                                                                                                                                      [100%]
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    [gw0] darwin -- Python 3.9.12 /Users/laobai/workspaces/testdemo01/venv/bin/python
    
        def test_a():
            time.sleep(2)
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:18: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 3.22s ===================================================================

    可以看到3个文件中的6个用例耗时3.22s

pytest-ordering

通过使用装饰器@pytest.mark.run(order=n)来自定义用例的执行顺序,其中n为用例执行的顺序,1,2,3等

  1. 安装

    $ pip install pytest-ordering
  2. 示例

    新建一个测试文件:test_04_demo.py

    import pytest
    
    
    class TestClass:
    
        @pytest.mark.run(order=4)
        def test_b(self):
            print("test_b")
    
        @pytest.mark.run(order=2)
        def test_a(self):
            print("test_a")
    
        @pytest.mark.run(order=1)
        def test_d(self):
            print("test_d")
    
        @pytest.mark.run(order=3)
        def test_c(self):
            print("test_c")
    
    
    if __name__ == '__main__':
        pytest.main(["test_04_demo.py"])

    执行测试:

    $ pytest test_04_demo.py -v
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
    cachedir: .pytest_cache
    metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
    collected 4 items                                                                                                                                                 
    
    test_04_demo.py::TestClass::test_d PASSED                                                                                                                   [ 25%]
    test_04_demo.py::TestClass::test_a PASSED                                                                                                                   [ 50%]
    test_04_demo.py::TestClass::test_c PASSED                                                                                                                   [ 75%]
    test_04_demo.py::TestClass::test_b PASSED                                                                                                                   [100%]
    
    ======================================================================== 4 passed in 0.02s ========================================================================

    测试结果是按照指定的顺序输出的

pytest-rerunfailures

pytest-rerunfailures失败重试插件,当执行出错时,会进行重试。(因为不是所有执行出错都是程序问题,有可能是网络问题等导致用例失败)

  1. 安装

    pip install pytest-rerunfailures
  2. 示例

    在执行pytest测试时,增加参数--reruns n (n表示重试次数),--reruns-delay n(每次重跑的等待时间,n表示等待n秒)

    $ pytest --reruns 2
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    plugins: rerunfailures-10.3, html-3.2.0, metadata-2.0.4
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py RRF.                                                                                                                                        [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    ============================================================== 1 failed, 5 passed, 2 rerun in 0.07s ===============================================================

    从上面的执行结果可以看出,对于测试失败的用例,重试了2次,第3次才置为失败状态。

    $ pytest --reruns 2 --reruns-delay 2
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py RRF.                                                                                                                                        [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            # time.sleep(2)
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:18: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    ============================================================== 1 failed, 5 passed, 2 rerun in 4.09s ===============================================================

pytest-html

生成简易的测试报告。在后续的内容会介绍功能更强大、使用更广泛的allure测试报告插件

  1. 安装

    pip install pytest-html
  2. 示例

     $ pytest --html=report.html
    ======================================================================= test session starts =======================================================================
    platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0
    rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
    plugins: html-3.2.0, metadata-2.0.4
    collected 6 items                                                                                                                                                 
    
    test_01_demo.py F.                                                                                                                                          [ 33%]
    test_02_demo.py ..                                                                                                                                          [ 66%]
    test_03_demo.py ..                                                                                                                                          [100%]
    
    ============================================================================ FAILURES =============================================================================
    _____________________________________________________________________________ test_a ______________________________________________________________________________
    
        def test_a():
            print("____test-a____")
            # 断言失败
    >       assert func(3) == 5
    E       assert 4 == 5
    E        +  where 4 = func(3)
    
    test_01_demo.py:17: AssertionError
    ---------------------------------------------------------------------- Captured stdout call -----------------------------------------------------------------------
    ____test-a____
    ------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/pytest_demo/report.html -------------------------------------
    ===================================================================== short test summary info =====================================================================
    FAILED test_01_demo.py::test_a - assert 4 == 5
    =================================================================== 1 failed, 5 passed in 0.07s ===================================================================

    打开生成的report.html如下图:

断言

测试的主要工作目标就是验证实际结果与预期结果是否一致。通常情况下,自动化测试都是通过断言来实现这一目标。

简单理解,断言相当于功能测试用例的预期结果,没写预期结果,或者预期结果写的不准确,那么这条用例是无效的,自动化测试用例也是一样的。

pytest使用assert关键字来进行断言。assert关键字后面可以接一个表达式,表达式的结果为True,那么断言通过,即测试用例执行成功,否则用例执行失败。

assert语句

pytest的assert常用的断言方法有如下几种:

  • 判断方法或函数的返回值是否为真:
    • assert xx :判断xx结果为真
    • assert not xx : 判断xx结果为假
  • 判断包含或不包含:
    • assert a in b : 判断b包含a
    • assert a not in b : 判断b不包含a
  • 判断是否相等:
    • assert a == b :判断a等于b
    • assert a != b :判断a不等于b
  • 判断类型是否符合:
    • assert isinstance(a ,int) :判断a是否是int类型数据
import pytest


def test_case1():
    x = 1
    assert x


def test_case2():
    x = 0
    assert not x


def test_case3():
    x = 'python'
    assert 'o' in x


def test_case4():
    x = 'python'
    assert 'i' not in x


def test_case5():
    x = 10
    assert x == 10


def test_case6():
    x = 10
    assert x != 1


def test_case7():
    x = 100
    assert isinstance(x, int)


if __name__ == '__main__':
    pytest.main()

执行结果:

$ pytest -vs test_06_demo.py 
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 7 items                                                                                                                                                 

test_06_demo.py::test_case1 PASSED
test_06_demo.py::test_case2 PASSED
test_06_demo.py::test_case3 PASSED
test_06_demo.py::test_case4 PASSED
test_06_demo.py::test_case5 PASSED
test_06_demo.py::test_case6 PASSED
test_06_demo.py::test_case7 PASSED

======================================================================== 7 passed in 0.02s ========================================================================

pytest.raises

在断言一些代码块或者函数时会引发意料之中的异常或者其他失败的异常,导致程序无法运行时,使用 raises 捕获匹配到的异常,可以继续让代码正常运行。

raises()方法主要有两个参数:

expected_exception:预期的异常,可以是单个异常,也可以是多个异常,多个异常需要组装成元组;

match:关键字或正则表达式匹配异常,预期异常信息和实际异常信息一致,可省略

示例1

import pytest

def test_case1():
    with pytest.raises(expected_exception=ZeroDivisionError, match='division by zero'):
        1 / 0

代码1 / 0已经预知程序会报ZeroDivisionError类型的异常,异常信息为division by zero

执行代码:

$ pytest -vs test_07_demo.py 
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 1 item                                                                                                                                                  

test_07_demo.py::test_case1 PASSED

======================================================================== 1 passed in 0.01s ========================================================================

PASSED表示,我程序执行报错情况与预期的报错情况一致,即运行结果正确。

示例2

import pytest

def test_case1():
    with pytest.raises((ZeroDivisionError, ValueError)) as excinfo:
        1 / 0
    assert excinfo.type is ZeroDivisionError
    assert 'division by zero' in str(excinfo.value)

执行结果:

 $ pytest -vs test_07_demo.py 
======================================================================= test session starts =======================================================================
platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
cachedir: .pytest_cache
metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
rootdir: /Users/laobai/workspaces/testdemo01/pytest_demo
plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
collected 1 item                                                                                                                                                  

test_07_demo.py::test_case1 PASSED

======================================================================== 1 passed in 0.01s ========================================================================

pytest.ini

pytest.ini是pytest单元测试框架的核心配置文件,一般存放在项目的根目录。这个文件的作用是通过读取配置信息,从而改变pytest默认的行为,按指定的方式来运行。

pytest.ini不能使用任何中文符号。

pytest.ini都能做什么:

  • 修改用例的命名规则

  • 配置日志格式

  • 添加标签,防止运行过程报警告错误

  • 执行执行用例的目录

  • 排除搜索目录

    1. 在项目的根目录新建一个文件:pytest.ini

      输入如下内容:

      [pytest]
      python_files = test_*.py *_test.py pls_*.py
      python_classes = Test* PLS*
      python_functions = test_* pls_*

      [pytest]标识当前ini文件为pytest的配置文件;

      pytest默认是执行所有以test_开头的文件,这里在pytest.ini在保留test_*开头的同时增加了pls_*开头的文件;

      pytest默认是执行所有以Test开头的类,这里在pytest.ini在保留Test*开头的同时增加了PLS*开头的类;

      pytest默认是执行所有以test_开头的方法/函数,这里在pytest.ini在保留test_*开头的同时增加了pls_*开头的方法/函数;

    2. 新建一个测试用例文件pls_01_demo.py

      import pytest
      
      
      class PLSClass:
      
          def pls_a(self):
              print("pls_a")
      
          def pls_b(self):
              print("pls_b")
      
      
      if __name__ == '__main__':
          pytest.main(["pls_01_demo.py"])

      执行用例

      $ pytest pls_01_demo.py -vs
      ======================================================================= test session starts =======================================================================
      platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
      cachedir: .pytest_cache
      metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
      rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini
      plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
      collected 2 items                                                                                                                                                 
      
      pls_01_demo.py::PLSClass::pls_a pls_a
      PASSED
      pls_01_demo.py::PLSClass::pls_b pls_b
      PASSED
      
      ======================================================================== 2 passed in 0.01s ========================================================================

      在项目根目录下新建一个文件夹testcases,将test_06_demo.py和pls_01_demo.py两个文件复制到testcases目录下。在命令行中切换到testcases执行测试用例

      $ pytest -vs
      ======================================================================= test session starts =======================================================================
      platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
      cachedir: .pytest_cache
      metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
      rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini
      plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
      collected 9 items                                                                                                                                                 
      
      pls_01_demo.py::PLSClass::pls_a pls_a
      PASSED
      pls_01_demo.py::PLSClass::pls_b pls_b
      PASSED
      test_06_demo.py::test_case1 PASSED
      test_06_demo.py::test_case2 PASSED
      test_06_demo.py::test_case3 PASSED
      test_06_demo.py::test_case4 PASSED
      test_06_demo.py::test_case5 PASSED
      test_06_demo.py::test_case6 PASSED
      test_06_demo.py::test_case7 PASSED
      
      ======================================================================== 9 passed in 0.04s ========================================================================

      全部9条用例均被执行,配置文件的内容有效。

    3. pytest.ini中增加addopts

      addopts是为pytest命令增加默认的参数,它的值是在命令行执行测试用例中添加参数,命令行中可以添加多个参数,addopts`后面的值就可以有多少个,用空格分隔

      修改pytest.ini如下:

      [pytest]
      python_files = test_*.py *_test.py pls_*.py
      python_classes = Test* PLS*
      python_functions = test_* pls_*
      addopts = -vs --html=report.html

      执行用例:

       $ pytest
      ======================================================================= test session starts =======================================================================
      platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
      cachedir: .pytest_cache
      metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
      rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini
      plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
      collected 9 items                                                                                                                                                 
      
      pls_01_demo.py::PLSClass::pls_a pls_a
      PASSED
      pls_01_demo.py::PLSClass::pls_b pls_b
      PASSED
      test_06_demo.py::test_case1 PASSED
      test_06_demo.py::test_case2 PASSED
      test_06_demo.py::test_case3 PASSED
      test_06_demo.py::test_case4 PASSED
      test_06_demo.py::test_case5 PASSED
      test_06_demo.py::test_case6 PASSED
      test_06_demo.py::test_case7 PASSED
      
      -------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/testcases/report.html --------------------------------------
      ======================================================================== 9 passed in 0.04s ========================================================================

      只使用pytest命令即可以输出详细信息及print信息,还生成了简易测试报告

    4. 指定/忽略执行目录

      目前执行测试用例是在命令行下,在各个目录下切换。而正常项目测试,都是基于项目的根目录进行执行,而测试用例会集中在一个或几个专门的目录中。

      testpasths是指定执行用例的路径

      [pytest]
      python_files = test_*.py *_test.py pls_*.py
      python_classes = Test* PLS*
      python_functions = test_* pls_*
      
      addopts = -vs --html=report.html
      
      testpaths = testcases

      在项目根目录下执行用例

      $ pwd
      /Users/laobai/workspaces/testdemo01
      
      $ ls
      assets       history.json pytest.ini   pytest_demo  report.html  testcases    unittestdemo venv
      
      $ pytest
      ======================================================================= test session starts =======================================================================
      platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
      cachedir: .pytest_cache
      metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
      rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini, testpaths: testcases
      plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
      collected 9 items                                                                                                                                                 
      
      testcases/pls_01_demo.py::PLSClass::pls_a pls_a
      PASSED
      testcases/pls_01_demo.py::PLSClass::pls_b pls_b
      PASSED
      testcases/test_06_demo.py::test_case1 PASSED
      testcases/test_06_demo.py::test_case2 PASSED
      testcases/test_06_demo.py::test_case3 PASSED
      testcases/test_06_demo.py::test_case4 PASSED
      testcases/test_06_demo.py::test_case5 PASSED
      testcases/test_06_demo.py::test_case6 PASSED
      testcases/test_06_demo.py::test_case7 PASSED
      
      ------------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/report.html -------------------------------------------
      ======================================================================== 9 passed in 0.04s ========================================================================

      norecursedirs是指忽略设定的目录下的用例

      [pytest]
      python_files = test_*.py *_test.py pls_*.py
      python_classes = Test* PLS*
      python_functions = test_* pls_*
      
      addopts = -vs --html=report.html
      
      ;testpaths = testcases
      norecursedirs = testcases

      执行用例的log这里不再贴了,用例比较多,占用太大篇幅。忽略testcases目录下的用例,执行pytest_demo和unittestdemo两个目录下的用例

      ================================================== 5 failed, 30 passed, 2 skipped, 5 warnings, 2 rerun in 7.47s ===================================================
    5. pytest.ini增加过滤warnings信息的设定

      [pytest]
      python_files = test_*.py *_test.py pls_*.py
      python_classes = Test* PLS*
      python_functions = test_* pls_*
      
      addopts = -vs --html=report.html
      
      filterwarnings = ignore::UserWarning
      
      testpaths = testcases
    6. pytest.ini增加日志记录的设定

      修改pytest.ini如下:

      [pytest]
      python_files = test_*.py *_test.py pls_*.py
      python_classes = Test* PLS*
      python_functions = test_* pls_*
      
      addopts = -vs --html=report.html
      
      filterwarnings = ignore::UserWarning
      
      testpaths = testcases
      
      log_cli = True
      log_date_format = %Y-%m-%d %H-%M-%S
      log_level = DEBUG
      log_format = %(asctime)s - %(filename)s - %(module)s - %(funcName)s - %(lineno)d -%(levelname)s - %(message)s
      log_file = ./logs/test.log

      在测试用例中增加log输出语句

      import logging
      import pytest
      
      def test_case1():
          logging.info("这是test_case1 测试用例日志")
          x = 1
          assert x
      
      
      def test_case2():
          logging.info("这是test_case2 测试用例日志")
          x = 0
          assert not x
      
      
      def test_case3():
          logging.info("这是test_case3 测试用例日志")
          x = 'python'
          assert 'o' in x
      
      
      def test_case4():
          logging.info("这是test_case4 测试用例日志")
          x = 'python'
          assert 'i' not in x
      
      
      def test_case5():
          logging.info("这是test_case5 测试用例日志")
          x = 10
          assert x == 10
      import logging
      import pytest
      
      class PLSClass:
      
          def pls_a(self):
              logging.info("这是pls_a 测试用例日志")
              print("pls_a")
      
          def pls_b(self):
              logging.info("这是pls_b 测试用例日志")
              print("pls_b")
      
      
      if __name__ == '__main__':
          pytest.main(["pls_01_demo.py"])

      执行用例

       $ pytest
      ======================================================================= test session starts =======================================================================
      platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
      cachedir: .pytest_cache
      metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
      rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini, testpaths: testcases
      plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
      collected 9 items                                                                                                                                                 
      
      testcases/pls_01_demo.py::PLSClass::pls_a 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - pls_01_demo.py - pls_01_demo - pls_a - 13 -INFO - 这是pls_a 测试用例日志
      pls_a
      PASSED
      testcases/pls_01_demo.py::PLSClass::pls_b 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - pls_01_demo.py - pls_01_demo - pls_b - 17 -INFO - 这是pls_b 测试用例日志
      pls_b
      PASSED
      testcases/test_06_demo.py::test_case1 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case1 - 11 -INFO - 这是test_case1 测试用例日志
      PASSED
      testcases/test_06_demo.py::test_case2 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case2 - 17 -INFO - 这是test_case2 测试用例日志
      PASSED
      testcases/test_06_demo.py::test_case3 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case3 - 23 -INFO - 这是test_case3 测试用例日志
      PASSED
      testcases/test_06_demo.py::test_case4 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case4 - 29 -INFO - 这是test_case4 测试用例日志
      PASSED
      testcases/test_06_demo.py::test_case5 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case5 - 35 -INFO - 这是test_case5 测试用例日志
      PASSED
      testcases/test_06_demo.py::test_case6 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case6 - 41 -INFO - 这是test_case6 测试用例日志
      PASSED
      testcases/test_06_demo.py::test_case7 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-08 23-53-29 - test_06_demo.py - test_06_demo - test_case7 - 47 -INFO - 这是test_case6 测试用例日志
      PASSED
      
      ------------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/report.html -------------------------------------------
      ======================================================================== 9 passed in 0.06s ========================================================================

      可以看到执行输出的信息中已经记录了设定格式的日志,并且在根目录下新建了log目录log日志并写入日志

    7. pytest.ini中注册管理Mark标签

      mark标签不在pytest.ini中注册不影响mark标签的使用,只是会在执行时报出大量的warnings信息。而注册mark标签后,就不会出现warnings信息。

      [pytest]
      python_files = test_*.py *_test.py pls_*.py
      python_classes = Test* PLS*
      python_functions = test_* pls_*
      
      addopts = -vs --html=report.html
      
      filterwarnings = ignore::UserWarning
      
      testpaths = testcases
      
      log_cli = True
      log_date_format = %Y-%m-%d %H-%M-%S
      log_level = DEBUG
      log_format = %(asctime)s - %(filename)s - %(module)s - %(funcName)s - %(lineno)d -%(levelname)s - %(message)s
      log_file = ./logs/test.log
      
      markers =
          webtest
          iostest
          androidtest

      修改pls_01_demo.py

      from mark import *
      
      class PLSClass:
      
          @iostest
          def pls_a(self):
              logging.info("这是pls_a 测试用例日志")
              print("pls_a")
      
          @androidtest
          def pls_b(self):
              logging.info("这是pls_b 测试用例日志")
              print("pls_b")
      
      
      if __name__ == '__main__':
          pytest.main(["pls_01_demo.py"])
      

      mark.py

      import pytest
      
      webtest = pytest.mark.webtest
      iostest = pytest.mark.iostest
      androidtest = pytest.mark.androidtest

      使用mark标签执行用例

      $ pytest -m iostest
      ======================================================================= test session starts =======================================================================
      platform darwin -- Python 3.9.12, pytest-7.2.0, pluggy-1.0.0 -- /Users/laobai/workspaces/testdemo01/venv/bin/python
      cachedir: .pytest_cache
      metadata: {'Python': '3.9.12', 'Platform': 'macOS-10.14.6-x86_64-i386-64bit', 'Packages': {'pytest': '7.2.0', 'pluggy': '1.0.0'}, 'Plugins': {'rerunfailures': '10.3', 'xdist': '3.1.0', 'html': '3.2.0', 'ordering': '0.6', 'metadata': '2.0.4'}, 'JAVA_HOME': '/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home'}
      rootdir: /Users/laobai/workspaces/testdemo01, configfile: pytest.ini, testpaths: testcases
      plugins: rerunfailures-10.3, xdist-3.1.0, html-3.2.0, ordering-0.6, metadata-2.0.4
      collected 9 items / 8 deselected / 1 selected                                                                                                                     
      
      testcases/pls_01_demo.py::PLSClass::pls_a 
      -------------------------------------------------------------------------- live log call --------------------------------------------------------------------------
      2022-12-09 00-11-43 - pls_01_demo.py - pls_01_demo - pls_a - 14 -INFO - 这是pls_a 测试用例日志
      pls_a
      PASSED
      
      ------------------------------------------- generated html file: file:///Users/laobai/workspaces/testdemo01/report.html -------------------------------------------
      ================================================================= 1 passed, 8 deselected in 0.02s =================================================================

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
 上一篇
Python单元测试框架——pytest(二) Python单元测试框架——pytest(二)
夹具是函数,它将在应用它的每个测试函数之前运行。Fixtures 用于向测试提供一些数据,例如数据库连接、要测试的 URL 和某种输入数据。因此,我们可以将fixture函数附加到测试中,而不是为每个测试运行相同的代码,它会在执行每个测试之前运行并将数据返回给测试。
2022-12-09
下一篇 
Python单元测试框架——unittest(二) Python单元测试框架——unittest(二)
unittest单元测试框架是受到JUnit的启发,与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化,配置共享和关机代码测试。支持将测试样例聚合到测试集中,并将测试与报告框架独立。
2022-11-26
  目录