简介
pytest是一个非常成熟的全功能的Python测试框架, 简单灵活, 容易上手, 具有很多第三方插件,并且可以自定义扩展.
安装
1 | pip install pytest |
使用
简单例子
先写个测试代码tmp.py
1
2
3
4
5
6
7
8def add(a, b):
return a + b
def test_add():
assert add(1, 1) == 2
def test_add_fail():
assert add(1, 2) == 2
使用方法1
usage: py.test [options] [file_or_dir] [file_or_dir] [...]
所以我们执行 pytest tmp.py
输出结果, 两个测试用例一个通过一个失败, 通过用绿色 . 表示, 失败红色 F 表示.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18=============================== test session starts ===============================
platform darwin -- Python 3.6.9, pytest-5.2.0, py-1.8.0, pluggy-0.13.0
rootdir: /Users/ruan/projects
plugins: pylint-0.15.1, mypy-0.5.0, celery-4.3.0
collected 2 items
tmp.py .F [100%]
==================================== FAILURES =====================================
__________________________________ test_add_fail __________________________________
def test_add_fail():
> assert add(1, 2) == 2
E assert 3 == 2
E + where 3 = add(1, 2)
tmp.py:92: AssertionError
=========================== 1 failed, 1 passed in 0.10s ===========================
用例查找规则
测试用例目录优先级: 命令行参数目录 > 配置文件中的testpaths配置项 > 当前目录
支持的配置文件: pytest.ini,tox.ini,setup.cfg
测试用例查找规则:
- 如果当前目录在包中, 则以该包的顶级目录作为工作目录(向上查找, 第一个不包含
__init__.py
的目录) - 递归遍历目录,除非目录指定了不递归参数
norecursedirs
- 在目录中查找匹配
test_*.py
或者*_test.py
的文件, 并以包名的全路径导入 - 查找以
Test
开头的类(该类不能有 init 方法), 的以test
为前缀的方法. - 查找以
test
为前缀的函数.
常用参数
1 | pytest --fixtures, --funcargs 查看可用的 fixtures |
使用示例
执行单个模块中的全部用例:
py.test test_mod.py
执行指定路径下的全部用例:
py.test somepath
执行匹配
stringexpr
表达式的用例:py.test -k stringexpr
运行指定模块中的某个用例:
pytest test_mod.py::test_func
运行某个类下的某个用例:
pytest test_mod.py::TestClass::test_method
执行测试用例时输出print内容:
pytest -s test_mod.py
编写测试用例
断言
1 | import pytest |
Fixtures
fixture
是 pytest 特有的功能,它用 pytest.fixture 标识,定义在函数前面, 起到依赖注入的作用.
在编写测试函数的时候,可以将此函数名称做为传入参数,pytest 将会以依赖注入方式,将该函数的返回值作为测试函数的传入参数。
1 | import pytest |
fixture接收scope
和autouse
参数
不同的scope表明了fixture的作用访问和执行先后顺序, 以下fixture执行顺序从上到下依次执行. 同级别是fixture按照依赖关系决定先后次序.
autouse参数决定了fixture是自动执行, 还是在被用做参数传入时才执行
scope='session'
: 会话级别, 测试开始时执行一次, 在整个测试的过程不变scope='module'
: 模块级别, 每个模块测试开始时执行一次, 在整个模块测试的过程不变scope='function', autouse=True
: 函数级别, 在每个函数开始前自动执行.scope='function'
: 函数级别(默认是这个级别).
setUp & tearDown
setup\teardown
是指在模块、函数、类开始运行以及结束运行时执行一些动作。
例如: 数据库连接管理, 临时文件清理.
pytest支持的setup\teardown
钩子
1 | # 模块级别 |
以上这些钩子, 也都可以用 fixture 的方式等效实现, 例如:
1 |
|
conftest.py
从广义理解,conftest.py
是一个本地的 per-directory
插件,在该文件中可以定义目录特定的 hooks 和 fixtures。
pytest
框架会在它测试的项目中寻找 conftest.py 文件,然后在这个文件中寻找针对整个目录的测试选项.
总结起来,conftest.py
文件大致有如下几种功能:
Fixtures: 用于给测试用例提供静态的测试数据,其可以被所有的测试用于访问,除非指定了范围
加载插件: 用于导入外部插件或模块:
1
pytest_plugins ="myapp.testsupport.myplugin"
测试根路径: 如果将 conftest.py 文件放在项目根路径中,则 pytest 会自己搜索项目根目录下的子模块,并加入到 sys.path 中,这样便可以对项目中的所有模块进行测试,而不用设置 PYTHONPATH 来指定项目模块的位置。
Markers
marker
的作用是,用来标记测试,以便于选择性的执行测试用例。
Pytest 提供了一些内建的 marker, 这里列了几个个人觉得有用的, 详细的请看文档1
2
3
4
5
6
7
8
9
10
11
@pytest.mark.skip(reason=None)
@pytest.mark.skipif(condition)
@pytest.mark.tryfirst
@pytest.mark.trylast
例子:1
2
3
4
5# 如果是window平台, 跳过该测试用例
class TestPosixCalls:
def test_function(self):
"will not be setup or run under 'win32' platform"
也可以自定义markers, 这些markers只是具有名称, 只起了标记作用, 通过-m
参数执行指定的marker的测试用例.
例如pytest -m hello
, 只执行test_one测试用例1
2
3
4
5
6
7
def test_one():
assert False
def test_two():
assert False
插件
通过pip安装, 例如pylint插件1
pip install pytest-pylint
通过添加参数来使用插件1
pytest --pylint tmp.py
常用插件
- pytest-randomly: 测试顺序随机
- pytest-xdist: 分布式测试
- pytest-cov: 生成测试覆盖率报告
- pytest-pep8: 检测代码是否符合 PEP8 规范
- pytest-pylint: 检测代码风格和错误
- pytest-html: 生成 html 报告
- pytest-rerunfailures: 失败重试
- pytest-timeout: 超时测试
- pytest-mypy: type hints 检查
配置文件
可以在项目根目录放置pytest.ini
来控制pytest的行为
例如:1
2
3
4
5
6
7
8
9
10[pytest]
addopts = --pylint --mypy ; 指定默认追加的参数
; testpaths = /home/test/ ; 测试用例路径
; minversion = 1.1 ; 依赖的pytest的最低版本
; norecursedirs = xx ; 不搜索测试用例的路径
; console_output_style ; 控制台测试报告格式
; 记录测试用例中用到的markers, 通过--markers参数可以显示出来
; markers =
; webtest: Run the webtest case
; hello: Run the hello case