Pytest 测试框架使用简单、插件丰富、功能强大,被广泛用于 Python 自动化测试。本文主要介绍一些 Pytest 的基本概念和使用方法。
1. 运行机制
第一步,Pytest 从命令行或文件中读取配置
第二步,在指定目录下查找并导入 conftest.py 文件
第三步,查找满足匹配条件的测试文件,通常是 test_
开头的 py 文件
第四步,执行 session 或 module 类型的 fixture
第四步,查找并执行类、函数中,定义的测试用例
2. pytest.ini 和 conftest.py 文件
首先看下测试的文件组织方式:
1
2
3
4
5
6
7
8
| / -
| - pytest.ini # pytest 的配置文件
| - tests
|
| - conftest.py # 全局通用的配置和功能
| - fun_module # 某个模块的测试
| - test_a.py # 该模块下的测试
| - conftest.py # 该模块下通用的配置和功能
|
执行 Pytest 时,不仅可以使用命令行传递运行时参数,还可以使用配置文件。Pytest 查找配置文件的顺序是:
1
2
3
4
5
| 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:
1
2
3
4
5
6
7
| [pytest]
# 指定测试目录
testpaths = tests
# 指定测试用例文件的命名格式
python_files = test_*.py
# 指定 conftest 文件的路径
pytest_plugins = tests
|
tests/conftest.py 全局依赖的配置
1
2
| def pytest_configure():
pass
|
运行测试时,执行命令:
3. pytest.fixture
fixture 是 Pytest 引入的概念,是 Pytest 提供的强大功能之一。下面来看看具体的用法:
3.1 直接作为常量使用
1
2
3
4
5
6
| @pytest.fixture()
def remote_api():
return 'success'
def test_remote_api(remote_api):
assert remote_api == 'success'
|
3.2 作为前置函数运行
fixture 允许在执行测试用例之前,先执行一些准备动作。
1
2
3
4
5
6
7
8
9
| 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,会话级,一次测试只执行一次,所有被找到的函数和方法都可用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| @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 不需要事先定义好就能使用,下面是一个例子:
1
2
3
4
5
6
7
| import pytest
A = pytest.mark.A
@A
def test_A():
assert True
|
一些内置的 marker :
- skip,跳过被装饰的测试用例
- skipif,传递一个条件判断,满足时跳过测试
- xfail,如果执行失败则认为是通过,成功则认为失败
- parametrize,批量给测试用例提供数据。如果 parametrize 的参数名称和 fixture 名称一样,会覆盖 fixture。
1
2
3
4
5
6
| @pytest.mark.parametrize('name',
['12345',
'abcdef',
'0a1b2c3'])
def test_name_length(passwd):
assert len(passwd) == 6
|
5. 常用插件
5.1 pytest-cov
pytest-cov 是一个自动检测测试覆盖率的插件。使用示例:
1
| # pytest --cov=myproj tests/
|
5.2 pytest-mock
mock 是为了屏蔽一些依赖项。依赖项应该有单独的测试用例,每一个测试只需要关注自身功能是否正常。使用示例:
1
2
3
4
5
6
7
8
9
10
11
12
| 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 格式测试报告的插件。使用示例:
1
| # pytest --html=report.html
|
5.4 pytest-django
pytest-django 为 Django 应用和项目添加了 Pytest 支持。具体来说,pytest-django 引入了使用 pytest fixture 测试 Django 项目的能力,并且比标准的 Django 测试套件运行得更快。