Pytest官方教程-04-断言的编写和报告

目录:

  1. 安装及入门
  2. 使用和调用方法
  3. 原有TestSuite使用方法
  4. 断言的编写和报告
  5. Pytest fixtures:清晰 模块化 易扩展
  6. 使用Marks标记测试用例
  7. Monkeypatching/对模块和环境进行Mock
  8. 使用tmp目录和文件
  9. 捕获stdout及stderr输出
  10. 捕获警告信息
  11. 模块及测试文件中集成doctest测试
  12. skip及xfail: 处理不能成功的测试用例
  13. Fixture方法及测试用例的参数化
  14. 缓存: 使用跨执行状态
  15. unittest.TestCase支持
  16. 运行Nose用例
  17. 经典xUnit风格的setup/teardown
  18. 安装和使用插件
  19. 插件编写
  20. 编写钩子(hook)方法
  21. 运行日志
  22. API参考
    1. 方法(Functions)
    2. 标记(Marks)
    3. 钩子(Hooks)
    4. 装置(Fixtures)
    5. 对象(Objects)
    6. 特殊变量(Special Variables)
    7. 环境变量(Environment Variables)
    8. 配置选项(Configuration Options)
  23. 优质集成实践
  24. 片状测试
  25. Pytest导入机制及sys.path/PYTHONPATH
  26. 配置选项
  27. 示例及自定义技巧
  28. Bash自动补全设置

断言的编写和报告

使用assert语句进行断言

pytest允许你使用标准的Pythonassert断言语句来验证测试中的期望结果和实际结果。 例如,你可以编写以下内容:

# test_assert1.py文件内容
def f():
    return 3

def test_function():
    assert f() == 4

来断言你的函数返回一个特定的值。 如果此断言失败,你将看到函数调用的返回值:

$ pytest test_assert1.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 item

test_assert1.py F                                                    [100%]

================================= FAILURES =================================
______________________________ test_function _______________________________

    def test_function():
>       assert f() == 4
E       assert 3 == 4
E        +  where 3 = f()

test_assert1.py:5: AssertionError
========================= 1 failed in 0.12 seconds =========================

pytest支持显示常见的包括调用,属性,比较以及二元和一元运算符子表达式的值 (参考:pytest执行Python测试失败报告示例)。 你可以在不使用繁琐的Python惯用构造样板代码的同时,不丢失断言失败的对比信息(内省信息)。

当然,你也可以像下面所示,指定断言失败的返回消息:

assert a % 2 == 0, "值为奇数,应为偶数"

这样将不会断言失败对比信息(内省信息),而只简单地在追溯信息中显示你指定的失败返回信息。
有关断言内省的更多信息,请参阅高级断言内省

异常断言

你可以像如下所示,使用pytest.raises作为上下文管理器来进行异常断言:

import pytest

def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0

如果需要访问实际的异常信息,你可以使用:

def test_recursion_depth():
    with pytest.raises(RuntimeError) as excinfo:
        def f():
            f()
        f()
    assert 'maximum recursion' in str(excinfo.value)

excinfo是一个ExceptionInfo实例,它是实际异常的装饰器。 其主要属性有.type.value.traceback三种
版本3.0已修改
在上下文管理器中,你可以使用参数message来指定自定义失败信息:

>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
...     pass
... Failed: Expecting ZeroDivisionError

如果你想编写适用于Python 2.4的测试代码,你还可以使用其他两种方法来测试预期的异常:

pytest.raises(ExpectedException, func, *args, **kwargs)
pytest.raises(ExpectedException, "func(*args, **kwargs)")

两者都可以对带任意参数的函数,断言是否出现了期望的异常:ExpectedException。 即使没有异常或出现了不同的异常,报告生成器也能输出一些有用的断言信息。

注意,也可以为pytest.mark.xfail指定一个“raises”参数,当引发异常时标记用例失败:

@pytest.mark.xfail(raises=IndexError)
def test_f():
    f()

对于你在代码中故意设置的异常,使用pytest.raises断言更加好用,而将@ pytest.mark.xfail与check函数一起使用对于已知未修复或依赖中的bug会更好。

此外,上下文管理器表单接受match关键字参数来测试正则表达式匹配中的异常(如unittest中的TestCase.assertRaisesRegexp方法):

import pytest

def myfunc():
    raise ValueError("Exception 123 raised")

def test_match():
    with pytest.raises(ValueError, match=r'.* 123 .*'):
        myfunc()

match变量后的正则表达式与使用re.search函数来进行匹配一致。 因此在上面的例子中,match ='123'不会引发异常。

警示断言

2.8版本新增
你可以使用pytest.warns检查代码是否引发了特定警告。

使用上下文对比

2.0版本新增
pytest可以在断言的比较中提供丰富的上下文信息。 例如:

# test_assert2.py文件内容

def test_set_comparison():
    set1 = set("1308")
    set2 = set("8035")
    assert set1 == set2

当你运行这个模块后

$ pytest test_assert2.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 item

test_assert2.py F                                                    [100%]

================================= FAILURES =================================
___________________________ test_set_comparison ____________________________

    def test_set_comparison():
        set1 = set("1308")
        set2 = set("8035")
>       assert set1 == set2
E       AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E         Extra items in the left set:
E         '1'
E         Extra items in the right set:
E         '5'
E         Use -v to get the full diff

test_assert2.py:5: AssertionError
========================= 1 failed in 0.12 seconds =========================

对大量用例进行了特定对比:

  • 长字符串断言:显示上下文差异
  • 长序列断言:显示第一个失败的索引
  • 字典断言:显示不同的key-value对
    有关更多示例,请参阅报告样例。

自定义断言对比信息

可以通过实现hook方法pytest_assertrepr_compare来在断言结果中添加你自己的详细说明信息。
pytest_assertrepr_compare(config, op, left, right) [源码]
返回失败断言表达式中的对比信息。

如果没有自定义对比信息,则返回None,否则返回一列字符串。 字符串将由换行符连接,但字符串中的任何换行符都将被转义。 请注意,除第一行外的所有行都将略微缩进,目的是将第一行作为摘要。
参数: config(pytest.config.Config* - pytest config 对象
例如,在conftest.py文件中添加以下钩子方法,可以为Foo对象提供了附加对比信息:

# conftest.py内容
from test_foocompare import Foo
def pytest_assertrepr_compare(op, left, right):
    if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
        return ['Foo实例对比:',
                '   值: %s != %s' % (left.val, right.val)]

现在,在测试模块使用

# test_foocompare.py内容
class Foo(object):
    def __init__(self, val):
        self.val = val

    def __eq__(self, other):
        return self.val == other.val

def test_compare():
    f1 = Foo(1)
    f2 = Foo(2)
    assert f1 == f2

运行这个测试模块你可以看到conftest.py文件中定义的自定义输出:

$ pytest -q test_foocompare.py
F                                                                    [100%]
================================= FAILURES =================================
_______________________________ test_compare _______________________________

    def test_compare():
        f1 = Foo(1)
        f2 = Foo(2)
>       assert f1 == f2
E       assert Foo实例对比:
E            值: 1 != 2

test_foocompare.py:11: AssertionError
1 failed in 0.12 seconds

高级断言内省

2.1版本新功能
报告有关失败断言的详细信息是通过在运行之前重写assert语句来实现的。 重写的断言语句将内省信息放入断言失败消息中。 pytest只重写测试收集过程直接发现的测试模块中的assert断言,因此在支持模块(非测试模块)中的断言,不会被重写

你可以在导入模块前通过调用register_assert_rewrite手动启用断言重写(比如可以在conftest.py这样使用)。

注意
pytest通过使用导入hook方法写入新的pyc文件来重写测试模块。 通常这种结构比较清晰。 但是,如果你混乱导入,导入的hook方法可能会受到干扰。
如果是这种情况,您有两种选择:
通过将字符串PYTEST_DONT_REWRITE添加到其docstring来禁用特定模块的重写。
使用--assert = plain禁用所有模块的重写。
此外,如果无法写入新的.pyc文件(如在只读文件系统或zip文件中),重写将无提示失败。
有关进一步的信息,课参阅:本杰明彼得森写的pytest的新断言改写的幕后故事。

版本2.1新功能:添加断言重写作为备用内省技术。
版本2.1更改:引入--assert选项。 弃用--no-assert--nomagic
版本3.0版更改:删除--no-assert和--nomagic选项。 删除--assert = reinterp`选项。

你可能感兴趣的:(Pytest官方教程-04-断言的编写和报告)