[跟着官方文档学pytest][三][学习笔记]
1.如何在测试中编写和报告断言
1.1.使用assert语句进行断言
pytest允许使用标准Python断言来验证Python测试中的期望和值。 例如,可以编写以下内容:
# content of test_assert1.py
def f():
return 3
def test_function():
assert f() == 4
断言函数返回某个值。如果此断言失败,将看到函数调用的返回值:
pytest支持显示最常见的子表达式的值,包括调用、属性、比较以及二元和一元运算符。(请参阅pytest的Python故障报告演示)。这允许在不丢失内省信息的情况下使用惯用的Python构造而无需样板代码。
但是,如果使用这样的断言指定消息:
assert a%2==0,"value was odd, should be even"
然后根本不会发生断言自省,消息将简单地显示在回溯中。
1.2.关于预期异常的断言
为了编写有关引发异常的断言,可以使用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。
可以将匹配关键字参数传递给上下文管理器,以测试正则表达式是否匹配异常的字符串表示(类似于unittest中的TestCase.assertRaisesRegex方法):
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将为你提供有用的输出。
请注意,也可以为pytest.mark.xfail指定一个“raises”参数,它以更具体的方式检查测试是否失败,而不仅仅是引发任何异常:
@pytest.mark.xfail(raises=IndexError)
def test_f():
f()
使用pytest.raises()对于你正在测试自己的代码故意引发的异常的情况可能会更好,而使用带有检查功能的@pytest.mark.xfail可能更适合记录未修复的错误(其中测试 描述“应该”发生什么)或依赖项中的错误。
1.3.关于预期警告的断言
可以使用pytest.warns检查代码是否引发特定警告。
1.4.利用上下文相关的比较
pytest对在遇到比较时提供上下文相关信息提供了丰富的支持。例如:
# content of test_assert4.py
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
assert set1 == set2
输出结果:
对许多情况进行了特殊比较:
- 比较长字符串:显示上下文差异
- 比较长序列:第一个失败的索引
- 比较字典:不同的条目
1.5.为失败的断言定义你自己的解释
可以通过实现pytest_assertrepr_compare钩子来添加自己的详细说明。
pytest_assertrepr_compare(config, op, left, right)
返回失败断言表达式中比较的说明。
返回None表示没有自定义说明,否则返回字符串列表。字符串将由换行符连接,但字符串中的任何换行符都将被转义。请注意,除第一行外,其他所有行都将略微缩进,目的是将第一行作为摘要。
参数:config(pytest.Config)-pytest配置对象
op(str)
left(object)
right(object)
返回类型:
Optional[List[str]]
例如,考虑在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
输出结果:
1.6.断言内省详细信息
有关失败断言的报告详细信息是通过在运行断言语句之前重写断言语句来实现的。重写的断言语句将自检信息放入断言失败消息中。pytest =仅重写其测试收集过程直接发现的测试模块,因此不会重写本身不是测试模块的支持模块中的断言。
可以通过在导入导入模块之前调用register_assert_rewrite来手动启用断言重写(执行此操作的一个好位置是在conftest.py中)。
1.6.1.断言重写磁盘上的缓存文件
pytest会将重写的模块写回磁盘进行缓存。可以通过将以下内容添加到conftest.py文件的顶部来禁用此行为(例如,避免在经常移动文件的项目中留下过时的.pyc文件):
import sys
sys.dont_write_bytecode=True
请注意,你仍然可以获得断言自省的好处,唯一的更改是.pyc文件不会缓存在磁盘上。
此外,如果重写无法写入新的.pyc文件(即在只读文件系统或zip文件中),则重写将以静默方式跳过缓存。
1.6.2.禁用断言重写
pytest通过使用导入钩子编写新的pyc文件,在导入时重写测试模块。大多数时候,这是透明的。但是,如果你自己使用导入机器,则导入钩子可能会干扰。
如果是这种情况,你有两种选择:
- 通过将字符串PYTEST_DONT_REWRITE添加到特定模块的文档字符串来禁用其重写。
- 使用–assert=plain禁用所有模块的重写。
- 点赞
- 收藏
- 关注作者
评论(0)