pytest测试入门

目录

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.skippytest.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_setuppytest_configurepytest_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应用;

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦