目录

    Pytest 测试框架使用简单、插件丰富、功能强大,被广泛用于 Python 自动化测试。本文主要介绍一些 Pytest 的基本概念和使用方法。

    1. 运行机制

    第一步,Pytest 从命令行或文件中读取配置

    第二步,在指定目录下查找并导入 conftest.py 文件

    第三步,查找满足匹配条件的测试文件,通常是 test_ 开头的 py 文件

    第四步,执行 session 或 module 类型的 fixture

    第四步,查找并执行类、函数中,定义的测试用例

    2. pytest.ini 和 conftest.py 文件

    首先看下测试的文件组织方式:

    / -
      | - pytest.ini  # pytest 的配置文件
      | - tests
        |
        | - conftest.py  # 全局通用的配置和功能
        | - fun_module   # 某个模块的测试
              | - test_a.py  # 该模块下的测试
              | - conftest.py  # 该模块下通用的配置和功能
    

    执行 Pytest 时,不仅可以使用命令行传递运行时参数,还可以使用配置文件。Pytest 查找配置文件的顺序是:

    path/pytest.ini
    path/setup.cfg     # must also contain [pytest] section to match
    path/tox.ini       # must also contain [pytest] section to match
    pytest.ini
    ...                # all the way down to the root
    

    通常在项目下放置 pytest.ini 文件,配置相关参数即可。

    pytest.ini:

    [pytest]
    # 指定测试目录
    testpaths  = tests
    # 指定测试用例文件的命名格式
    python_files = test_*.py
    # 指定 conftest 文件的路径
    pytest_plugins = tests
    

    tests/conftest.py 全局依赖的配置

    def pytest_configure():
        pass
    

    运行测试时,执行命令:

    # pytest . -s
    

    3. pytest.fixture

    fixture 是 Pytest 引入的概念,是 Pytest 提供的强大功能之一。下面来看看具体的用法:

    3.1 直接作为常量使用

    @pytest.fixture()
    def remote_api():
        return 'success'
    
    def test_remote_api(remote_api):
        assert remote_api == 'success'
    

    3.2 作为前置函数运行

    fixture 允许在执行测试用例之前,先执行一些准备动作。

    import pytest
    
    @pytest.fixture()
    def before():
        pass
    
    @pytest.mark.usefixtures("before")
    def test_1():
        pass
    

    通过 autouse 和 scope,可以完成很多测试用例的准备工作。

    3.3 作用域 - scope

    通过 scope 参数声明作用域,可选项有四个,默认值为 function:

    • function,函数级,每个测试函数都会执行一次
    • class, 类级别,每个测试类执行一次,所有方法都可以使用
    • module,模块级,每个模块执行一次,模块内函数和方法都可使用
    • session,会话级,一次测试只执行一次,所有被找到的函数和方法都可用
    @pytest.fixture(scope='function')
    def func_scope():
        pass
    
    @pytest.fixture(scope='module')
    def mod_scope():
        pass
    
    @pytest.fixture(scope='session')
    def sess_scope():
        pass
    
    @pytest.fixture(scope='class')
    def class_scope():
        pass
    

    4. pytest.mark

    pytest.mark 的作用是对测试用例进行标记,从而更精细化地控制测试用例。

    marker 不需要事先定义好就能使用,下面是一个例子:

    import pytest
    
    A = pytest.mark.A
    
    @A
    def test_A():
        assert True
    

    一些内置的 marker :

    • skip,跳过被装饰的测试用例
    • skipif,传递一个条件判断,满足时跳过测试
    • xfail,如果执行失败则认为是通过,成功则认为失败
    • parametrize,批量给测试用例提供数据。如果 parametrize 的参数名称和 fixture 名称一样,会覆盖 fixture。
    @pytest.mark.parametrize('name',
                          ['12345',
                           'abcdef',
                           '0a1b2c3'])
    def test_name_length(passwd):
        assert len(passwd) == 6
    

    5. 常用插件

    5.1 pytest-cov

    pytest-cov 是一个自动检测测试覆盖率的插件。使用示例:

    # pytest --cov=myproj tests/
    

    5.2 pytest-mock

    mock 是为了屏蔽一些依赖项。依赖项应该有单独的测试用例,每一个测试只需要关注自身功能是否正常。使用示例:

    import os
    
    class UnixFS:
    
        @staticmethod
        def rm(filename):
            os.remove(filename)
    
    def test_unix_fs(mocker):
        mocker.patch('os.remove')
        UnixFS.rm('file')
        os.remove.assert_called_once_with('file')
    

    5.3 pytest-html

    pytest-html 是一个能自动生成 HTML 格式测试报告的插件。使用示例:

    # pytest --html=report.html
    

    5.4 pytest-django

    pytest-django 为 Django 应用和项目添加了 Pytest 支持。具体来说,pytest-django 引入了使用 pytest fixture 测试 Django 项目的能力,并且比标准的 Django 测试套件运行得更快。