简介
pytest:帮助你写出更好的程序:
- 开源,免费。
- 升级pytest,严格的向后兼容性。
- 丰富的第三方插件。
- 内置assert断言。
安装
pip install -U pytest # or easy_install -U pytest
查看版本
py.test --version
也许你已经使用unittest和doctest写了很多的测试用例了,不过你可以直接使用pytest运行他们。
pytest fixtures
fixture可以声明function,module,fixture。也可以使用xunit的fixture的格式,setup和teardown。使用fixtures作为function的参数使用。
分类
像unittest有setup和teardown固定装置,pytest扩展了该功能。
- 模块的固定装置:setup_module/teardown_module
- 类的固定装置:setup_class/teardown_class
- 可替代的类级别的固定装置:setup/teardown
- Module级别方法装置:setup_function
- Class级别方法装置:setup_method
pytest_fixtrue.py
def setup_module() def teardown_module() def setup_function() def teardown_funcion() def module_funtion() class class_name: def setup() def teardown() def setup_class() def teardown_class() def setup_method() def teardown_method() def class_function()
运行后,执行顺序:
setup_module() setup_function() module_function() teardown_funtion() setup_class() setup_method() setup() class_function() teardown() teardown_method() teardown_class() teardown_module()
需要哪些装置写哪些。
fixture参数scope
scope控制fixtrue调用的频率,默认是function。可选的有:
- function:每个测试函数之前执行一次
- class:每个测试类之前执行一次
- module:每个module之前执行一次
- session:每次session之前执行一次,即每次测试执行一次
fixture参数Request
使用request参数,当要添加finilizer到fixture中。
相当于pytest内置的一个fixture,需要传递给fixture,才能识别finilizer。
def test_data(request): ......
fixture参数params
@pytest.fixture( params=[1,2,3] ) def test_data(request): return request.param def test_not_2(test_data): print('test_data: %s' % test_data) assert test_data != 2
依次循环传入params中的每个值作为参数。
参数化方法
pytest支持在多个完整测试参数化方法:
- pytest.fixture(): 在fixture级别的function处参数化
- @pytest.mark.parametrize:允许在function或class级别的参数化,为特定的测试函数或类提供了多个argument/fixture设置。
- pytest_generate_tests:可以实现自己的自定义动态参数化方案或扩展。
@pytest.mark.parametrize
内建的pytest.mark.parametrize装饰器可以参数化测试函数的参数。
import pytest @pytest.mark.parametrize("test_input,expected", [ ("3+5", 8), ("2+4", 6), ("6*9", 42), ]) def test_eval(test_input, expected): assert eval(test_input) == expected
conftest文件多个py共享fixture
如果一个fixture需要共享,不用在每个py文件中写一遍,写在conftest.py文件中就好。
在module/class/session件share一个fixture)
def test_ehlo(smtp): response, msg = smtp.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg assert 0 # for demo purposes def test_noop(smtp): response, msg = smtp.noop() assert response == 250 assert 0 # for demo purposes
共享一个smtp实例。
fixture终止/执行teardown
当最后一个需要fixture的test执行结束后,可指定执行终止的代码。可以传递一个request作为参数,在结束后回调fin函数
request.addfinalizer(fin)
fin为自定义的回调函数。
执行用例
pytest将运行所有在当前目录及其子目录的test_.py or _test.py.形式。遵循标准测试发现test discovery规则。
组织测试:当有许多的测试用例了,注意使用class或者module组织测试用例。
python -m pytest [...]
或者
py.test [...]
查看选项名称,环境变量
py.test --fixtures # show available builtin function arguments py.test -h | --help # show help on command line and config file options
选择要运行的测试
pytest test_mod.py # 运行test_mod.py所有测试 pytest somepath #运行某个路径下的所有测试 pytest -k stringexpr # 运行匹配stringexpr的测试函数 pytest test_mod.py::test_func # 运行test_mod.py模块中的test_func测试函数 pytest test_mod.py::TestClass::test_method # TestClass类下的test_method测试方法
忽略查找路径 --ingore
tests/ |-- example | |-- test_example_01.py | |-- test_example_02.py | '-- test_example_03.py |-- foobar | |-- test_foobar_01.py | |-- test_foobar_02.py | '-- test_foobar_03.py '-- hello '-- world |-- test_world_01.py |-- test_world_02.py '-- test_world_03.py
忽略 test_footbar_03.py
--ignore=tests/foobar/test_foobar_03.py
忽略 整个hello目录
--ignore=tests/hello/
修改Python的trace输出
py.test --showlocals # show local variables in tracebacks py.test -l # show local variables (shortcut) py.test --tb=auto # (default) 'long' tracebacks for the first and last # entry, but 'short' style for the other entries py.test --tb=long # exhaustive, informative traceback formatting py.test --tb=short # shorter traceback format py.test --tb=line # only one line per failure py.test --tb=native # Python standard library formatting py.test --tb=no # no traceback at all
请求一个唯一的临时目录
def test_needsfiles(tmpdir): print (tmpdir) assert 0
pytest会寻找并创建一个fixture在调用其他test方法时。
在每个测试方法前面创建一个临时的目录。
查看内建的fixture:
py.test --fixtures # shows builtin and custom fixtures
运行顺序说明
pytest按照在.py中的出现位置(从上到下)执行用例,可以改变执行顺序
使用pytest-ordering,pytest-dependency
打印结果
pytest在通过测试时会忽略中间的print信息。要打印则需要assert为false,打印好看用json来格式化下:
import json print json.dumps(posts_local, indent=4) assert False
也可以使用pprint.pprint()
处理失败
使用-r可以查看skips和xfails的详细信息。
Skip
预期测试能通过,除非环境(如:错误的python解释器,缺少依赖)阻止其不能正常的运行。
跳过一个测试函数
通过skip装饰器:
@pytest.mark.skip(reason="no way of currently testing this") def test_the_unknown(): ...
设置跳过测试的条件
小于python3.3解释器运行时,跳过:
import sys @pytest.mark.skipif(sys.version_info < (3,3), reason="requires python3.3") def test_function(): ...
在module之间共享跳过条件
# content of test_mymodule.py import mymodule minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1), reason="at least mymodule-1.1 required") @minversion def test_function(): ...
导入跳过
# test_myothermodule.py from test_mymodule import minversion @minversion def test_anotherfunction(): ...
对于大型测试套件是一个好主意,有一个文件,你定义的标记然后一直应用在整个测试套件。
跳过一个类的所有测试函数或模块
在类上使用skipif :
@pytest.mark.skipif(sys.platform == 'win32', reason="does not run on windows") class TestPosixCalls: def test_function(self): "will not be setup or run under 'win32' platform"
跳过所有测试方法:在global level使用pytestmark 名称
# test_module.py pytestmark = pytest.mark.skipif(...)
xfail
测试可以运行,但是你期望其失败,因为有实现上的问题。
def xfail(condition=None, *, reason=None, raises=None, run=True, strict=False):
一个测试函数标记为失败
使用xfail marker:
@pytest.mark.xfail def test_function(): ...
这样,会运行该测试,但失败了无输出信息。
设定失败的理由
def test_hello6(): pytest.xfail("reason")
测试报告
pytest可以方便的生成测试报告,即可以生成HTML的测试报告,也可以生成XML格式的测试报告用来与持续集成工具集成。
查看pytest-html