断言相关

断言assert声明

  • pytest允许您使用标准 Pythonassert来验证 Python 测试中的期望值和值。例如,您可以编写以下内容:

    # content of 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-7.x.y, pluggy-1.x.y
    rootdir: /home/sweet/project
    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:6: AssertionError
    ========================= short test summary info ==========================
    FAILED test_assert1.py::test_function - assert 3 == 4
    ============================ 1 failed in 0.12s =============================
    

    pytest支持显示最常见的子表达式的值,包括调用、属性、比较以及二元和一元运算符。(请参阅pytest 的 Python 故障报告演示)。这允许您在不丢失内省信息的情况下使用惯用的 Python 构造而无需样板代码。

当断言失败指定失败信息

assert a % 2 == 0, "value was odd, should be even"

然后根本不会发生断言自省,消息将简单地显示在回溯中。
有关断言自省的更多信息,请参阅断言自省详细信息。

关于预期异常的断言

  • 为了编写有关引发异常的断言,您可以 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

  • 您可以将match关键字参数传递给上下文管理器,以测试正则表达式是否匹配异常的字符串表示形式(类似于TestCase.assertRaisesRegex方法 from unittest):

    import pytest
    
    
    def myfunc():
       raise ValueError("Exception 123 raised")
    
    
    def test_match():
       with pytest.raises(ValueError, match=r".* 123 .*"):
           myfunc()
    

    match方法的 regexp 参数与函数匹配re.search ,因此在上面的示例match='123'中也可以正常工作。

  • 有一种替代形式的pytest.raises()函数,您可以传递一个函数,该函数将使用给定的*args**kwargs断言引发给定的异常:

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

    如果出现无异常或错误异常等故障,reporter 将为您提供有用的输出。

  • 请注意,也可以为 指定一个“raises”参数 pytest.mark.xfail,它以一种更具体的方式检查测试是否失败,而不仅仅是引发任何异常:

    # 也可以捕捉报错的异常
    @pytest.mark.xfail(raises=IndexError)
    def test_f():
        f()
    

    pytest.raises()对于您正在测试自己的代码故意引发的异常的情况, 使用可能会更好,而使用 @pytest.mark.xfail检查功能可能更适合记录未修复的错误(测试描述“应该”发生什么)或依赖项中的错误.

关于预期警告的断言

您可以使用pytest.warns检查代码是否引发了特定警告 。

  • 利用上下文敏感的比较

    # content of 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-7.x.y, pluggy-1.x.y
    rootdir: /home/sweet/project
    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 more diff
    
    test_assert2.py:4: AssertionError
    ========================= short test summary info ==========================
    FAILED test_assert2.py::test_set_comparison - AssertionError: assert {'0'...
    ============================ 1 failed in 0.12s =============================
    

    对许多情况进行了特殊比较:

    • 比较长字符串:显示上下文差异
    • 比较长序列:第一个失败的索引
    • 比较字典:不同的条目
      有关更多示例,请参阅报告演示。

为失败的断言定义自己的解释

  • 可以通过实现pytest_assertrepr_compare钩子来添加您自己的详细解释。详情见

  • 例如,考虑在conftest.py 文件中添加以下钩子,它为对象提供了另一种解释Foo:

    # content of 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 [
                "Comparing Foo instances:",
                "   vals: {} != {}".format(left.val, right.val),
            ]
    
  • 给定这个测试模块:

    # 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_compare():
        f1 = Foo(1)
        f2 = Foo(2)
        assert f1 == f2
    
  • 您可以运行测试模块并获取 conftest 文件中定义的自定义输出:

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

    def test_compare():
        f1 = Foo(1)
        f2 = Foo(2)
>       assert f1 == f2
E       assert Comparing Foo instances:
E            vals: 1 != 2

test_foocompare.py:12: AssertionError
========================= short test summary info ==========================
FAILED test_foocompare.py::test_compare - assert Comparing Foo instances:
1 failed in 0.12s

断言自省

通过在运行之前重写断言语句来报告有关失败断言的详细信息。重写的断言语句将自省信息放入断言失败消息中。 pytest仅重写其测试收集过程直接发现的测试模块,因此支持模块中的断 > 言本身不是测试模块将不会被重写。
您可以在导入模块之前通过调用 register_assert_rewrite手动为导入的模块启用断言重写 (这样做> 的好地方是在您的 root 中conftest.py)。
有关更多信息,B​​enjamin Peterson 撰写了pytest 新断言重写的幕后报道。

断言重写缓存文件在磁盘上

  • pytest将重写的模块写回磁盘进行缓存。您可以通过将其添加到文件顶部来禁用此行为(例如,避免在经常.pyc移动文件的项目中留下陈旧的文件)conftest.py
    import sys
    
    sys.dont_write_bytecode = True
    

    请注意,您仍然可以获得断言自省的好处,唯一的变化是.pyc文件不会缓存在磁盘上。
    此外,如果无法写入新.pyc文件,即在只读文件系统或 zip 文件中,重写将静默跳过缓存。

禁用断言重写

  • pytest通过使用导入钩子写入新pyc文件来重写导入时的测试模块。大多数情况下,这是透明的。但是,如果您自己使用导入机器,导入钩子可能会干扰。
  • 如果是这种情况,您有两种选择:
    • 通过将字符串添加到其文档字符串来禁用特定模块的重写 PYTEST_DONT_REWRITE
    • 使用 禁用所有模块的重写--assert=plain
posted @ 2022-06-14 14:11  zhq9  阅读(20)  评论(0编辑  收藏  举报