1. pytest简介
Pytest 是一个比较成熟且功能完备的 Python 测试框架。其提供完善的在线文档,并有着大量的第三方插件和内置帮助,适用于许多小型或大型项目。Pytest 灵活易学,打印调试和测试执行期间可以捕获标准输出,适合简单的单元测试到复杂的功能测试。还可以执行 nose, unittest 和 doctest 风格的测试用例,甚至 Django 和 trial。支持良好的集成实践, 支持扩展的 xUnit 风格 setup,支持非 python 测试。支持生成测试覆盖率报告,支持 PEP8 兼容的编码风格。
2. pytest安装
pip install pytest
3. pytest基本使用
3.1 pytest命令行参数
py.test [options] [file_or_dir] [file_or_dir] [...]
3.1.1 常用参数
py.test -h,--help # 查看命令行和配置文件帮助
pytest --markers # 查看所有的标记标记
-l,--showlocals #在traceback中显示本地变量
-q,--quiet #减少输出
--collect-only:只收集测试用例,不执行测试用例
-v,--verbose #详细输出
# 调试输出
-x:遇到用例失败后,停止执行后面的用例
--maxfail=num:最多允许num个测试用例失败,后面的用例不再执行
--pdb:当测试用例失败时,进入调试模式
--tb=style:指定traceback的输出风格
- long:完整的traceback信息
- short:只显示每个失败用例的第一行信息
- no:不显示traceback信息
- native:使用C语言的格式化1输出traceback信息
-m MARKEXPR:只运行标记了指定标记的用例
3.2 执行选择用例
# 执行单个模块中的全部用例
pytest test_module.py
# 执行单个类中的全部用例
pytest test_module.py::TestClass
# 执行单个方法
pytest test_module.py::TestClass::test_method
# 执行指定module中的某个测试函数
pytest test_module.py::test_func
# 执行指定路径下的全部用例
pytest test_dir/
# 执行字符串匹配的用例
pytest -k "string_expression"
# 执行标记为smoke的用例
pytest -m smoke
# 执行标记为smoke且用例名中包含add的用例
pytest -k "add and smoke"
# 执行标记为smoke或用例名中包含add的用例
pytest -k "add or smoke"
# 导入模块,使用其文件系统中的路径来查找和执行用例,执行pkg包中的全部用例
pytest --pyargs pkg # pyargs参数,pkg是包名
3.3 断言
通常情况下使用 assert 语句就能对大多数测试进行断言。对于异常断言,可以使用上下文管理器 pytest.raises:
# 断言异常,可以使用上下文管理器**pytest.raises**
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
1 / 0 # 在这里抛出异常,断言成功
# 还可以使用pytest.raises的context manager的value属性来获取异常信息
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError) as excinfo: #excinfo是异常信息
1 / 0
assert 'division by zero' in str(excinfo.value)
# 捕捉异常信息还包括
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError,message=" integer division or modulo by zero"):
1 / 0
# 对于警告断言,可以使用pytest.warns(上下文管理器):
# UserWarning是警告类型,match是匹配的字符串
with pytest.warns(UserWarning,match='must be 0 or None'):
warnings.warn("my warning", UserWarning)
with pytest.warns(RuntimeWarning):
warnings.warn("my warning", RuntimeWarning)
对于自定义的assert比较断言,还可以通过在conftest.py中定义pytest_assertrepr_compare函数来实现:
# content of test_foocompare.py
class Foo:
def __init__(self, val):
self.val = val
def __eq__(self, other):
return self.val == other.val
def test():
assert 1 == 1
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
f3 = Foo(1)
assert f1 == f3
assert f1 == f2
# content of conftest.py
def pytest_assertrepr_compare(op, left, right):
from test_foocompare import Foo
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
return ['Comparing Foo instances:', 'vals: %s != %s' % (left.val, right.val)]
如果需要手动设置断言失败的信息,可以使用pytest.fail函数:
def test_fail():
pytest.fail("manual fail")
def test_sys_version():
# skip test if not running on Python 3.x
if sys.version_info < (3, 0):
pytest.fail("requires python3")
3.4 跳过用例
# 跳过用例
@pytest.mark.skip(reason="no way of currently testing this")
def test_function():
pass
# 跳过用例,如果条件为真
@pytest.mark.skipif(sys.version_info < (3, 0), reason="requires python3")
def test_function():
pass
# 跳过用例,如果条件为假
@pytest.mark.skipif(sys.version_info >= (3, 0), reason="requires python3")
def test_function():
pass
使用 pytest.skip 和 pytest.xfail 能够实现跳过测试的功能,skip 表示直接跳过测试,而 xfail 则表示存在预期的失败,但两者的效果差不多:
def test_skip_and_xfail():
if sys.version_info[0] < 3:
pytest.skip("requires python3")
else:
pytest.xfail("xfail on python3")
pytest.importorskip 函数可以用来跳过测试,如果导入模块失败:
# 跳过用例,如果导入模块失败
def test_importorskip():
pytest.importorskip("this_module_does_not_exist")
# 要求导入的模块的版本号大于等于指定的版本号
def test_importorskip():
pytest.importorskip("this_module_does_not_exist", minversion="1.0")
断言近似相等时,可以使用pytest.approx函数:
def test_approx():
assert 0.1 + 0.2 == pytest.approx(0.3)
def test_approx2():
assert 2.2==pytest.approx(2.3,rel=0.1) # rel是相对误差
3.5 conftest.py
从广义理解,conftest.py是一个配置文件,可以用来配置pytest的运行环境,从狭义理解,conftest.py是一个pytest的插件,可以用来定义pytest的fixture、hook函数、命令行参数等。conftest.py文件可以放在测试用例所在的目录下,也可以放在测试用例所在目录的上一级目录下,如果放在上一级目录下,那么该目录下的所有子目录都会加载该conftest.py文件。
conftest.py文件功能如下:
- 定义fixture,用于给测试用例提供静态的测试数据,其可以被所有的测试用于访问,除非指定了范围。
- 加载插件,用于导入外部插件或者模块:
import pytest pytest_plugins = "myapp.testsupport.myplugin"
- 定义钩子:用于定义pytest的钩子函数,比如pytest_runtest_setup、pytest_configure、pytest_runtest_teardown等。
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--full", action="store_ture",
help="run full test")
# content of test.py
@pytest.mark.skipif(not pytest.config.getoption("--runslow"))
def test_func_slow_1():
"""当在命令行执行 --runslow 参数时才执行该测试"""
print 'skip slow'
3.6 Fixture
Fixture 是 pytest 特有的功能,它用 pytest.fixture 标识,定义在函数前面。在编写测试函数的时候,可以将此函数名称做为传入参数,pytest 将会以依赖注入方式,将该函数的返回值作为测试函数的传入参数。
@pytest.fixture
def login():
print("登录操作")
username
return username
def test_soso(login):
print(f"搜索{login}")
def test_cart(login):
print(f"添加购物车{login}")
fixture 的作用域 fixture 的作用域默认是函数级别的,也就是说,每个测试用例都会执行一次 fixture 函数。如果想要在多个测试用例中使用 fixture 函数的返回值,可以将 fixture 的作用域设置为模块级别.
- function:默认值,每个测试用例都会执行一次 fixture 函数;
- module: 每个模块加载前执行一次;
- session:每个会话加载前执行一次;
3.7 Markers
marker 是 pytest 特有的功能,它用于给测试用例打标签,可以用于过滤测试用例,也可以用于给测试用例分类。
# 跳过测试
@pytest.mark.skip(reason="no way of currently testing this")
# 满足某个条件时跳过测试
@pytest.mark.skipif(sys.version_info < (3, 3), reason="requires python3.3+")
# 期望测试失败
@pytest.mark.xfail(condition, reason, run=True, raises=None, strict=False)
# 参数化测试函数,给测试用例传入参数,供运行时填充到测试中
@pytest.mark.parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
# 执行某一类测试用例
pytest -m smoke
# 测试顺序
@pytest.mark.run(order=1)
# 让测试尽早地被执行
@pytest.mark.tryfirst
# 让测试尽晚地被执行
@pytest.mark.trylast
3.8 生成测试报告
3.9 第三方插件
pytest-randomly: 测试顺序随机
pytest-ordering: 测试顺序控制
pytest-xdist: 分布式测试
pytest-cov: 生成测试覆盖率报告
pytest-rerunfailures: 失败重试
pytest-timeout: 超时测试,允许在命令行指定超时时间,或者直接在测试代码中标注超时时间。
pytest-repeat: 重复测试(可以使用–count参数指定重复次数)
pytest-instafail: 测试失败时立即停止,查看错误的详细信息。
pytest-sugar: 优化测试报告,显示色彩和进度条
pytest-html: 为测试生成HTML报告
pytest --html=report.html
静态分析用的插件
pytest-pycodestyle:
pytest-pep8: 检测代码是否符合 PEP8 规范
pytest-flakes: 检测代码风格
Web开发用的插件:
Web项目有特定的测试逻辑,pytest也不能让测试变简单,但是有几个插件可以帮忙。
pytest-django: 用于测试 Django 项目,Django是很流行的基于Python的web开发框架,它本身包含用于测试的hook函数,允许测试Django应用的各个组件,Django测试使用的是unittest;
pytest-selenium:借助浏览器完成自动化测试,比如说启动一个web浏览器,打开网址URL,运行web应用;
pytest-flask:测试Flask应用,Flask是一个轻量级的web框架,pytest-flask插件提供了一组很有用的fixture来帮助Flask应用;