函数(Functions)
pytest.approx
断言两个数字(或两组数字)在某个容差范围内彼此相等。
由于浮点运算的复杂性,我们直觉期望相等的数字并不总是如此:
0.1 + 0.2 == 0.3
False
编写测试时通常会遇到此问题,例如,确保浮点值是您期望的值。处理此问题的一种方法是断言两个浮点数等于某个适当的容差范围内:
abs((0.1 + 0.2) - 0.3) < 1e-6
True
但是,这样的比较写作起来既乏味又难以理解。此外,通常不鼓励像上面这样的绝对比较,因为没有适合所有情况的容忍度。1e-6
对周围的数字有好处1
,但对于非常大的数字而言太小而对于非常小的数字而言太大。最好将公差表示为预期值的一小部分,但这样的相对比较更难以正确和简洁地书写。
该approx
班采用语法是尽可能直观执行浮点比较:
from pytest import approx
>>> 0.1 + 0.2 == approx(0.3)
True
相同的语法也适用于数字序列:
(0.1 + 0.2,0.2 + 0.4) == approx((0.3,0.6))
True
字典值:
{'a': 0.1 + 0.2,'b': 0.2 + 0.4} == approx({'a': 0.3,'b': 0.6})
True
numpy
数组:
import numpy as np
>>> np.array([0.1,0.2]) + np.array([0.2,0.4]) == approx(np.array([0.3,0.6]))
True
对于numpy
标量的数组:
import numpy as np
>>> np.array([0.1,0.2]) + np.array([0.2,0.1]) == approx(0.3)
True
默认情况下,approx
将1e-6
其预期值的相对容差(即百万分之一)内的数字视为相等。如果预期值是这样的0.0
,那么这种处理会产生令人惊讶的结果,因为除了0.0
本身之外什么都不是0.0
。为了不那么令人惊讶地处理这种情况,approx
还要考虑在1e-12
其预期值的绝对容差内的数字是相等的。Infinity和NaN是特殊情况。无论相对容差如何,无穷大只被视为与自身相等。默认情况下,NaN不被视为等于任何内容,但您可以通过将nan_ok
参数设置为True来使其等于自身。(这是为了便于比较使用NaN的数组表示“无数据”。)
通过将参数传递给approx
构造函数,可以更改相对容差和绝对容差:
1.0001 == approx(1)
False
>>> 1.0001 == approx(1,rel=1e-3)
True
>>> 1.0001 == approx(1,abs=1e-3)
True
如果您指定abs
但不指定rel
,则比较将不会考虑相对容差。换句话说,1e-6
如果超过指定的绝对容差,则默认相对容差范围内的两个数字仍将被视为不相等。如果同时指定两个abs
和rel
,如果满足任一公差,则数字将被视为相等:
1 + 1e-8 == approx(1)
True
>>> 1 + 1e-8 == approx(1,abs=1e-12)
False
>>> 1 + 1e-8 == approx(1,rel=1e-6,abs=1e-12)
True
如果您正在考虑使用approx
,那么您可能想知道它与比较浮点数的其他好方法的比较。所有这些算法都基于相对和绝对容差,并且应该在大多数情况下达成一致,但它们确实存在有意义的差异:
math.isclose(a,b,rel_tol=1e-9,abs_tol=0.0)
:True如果相对误差满足WRT无论是a
或b
,或者如果绝对容差得到满足。因为相对容差是a
和两者一起计算的b
,所以该测试是对称的(即既不是a
也不b
是“参考值”)。如果要进行比较,则必须指定绝对容差,0.0
因为默认情况下没有容差。仅在python> = 3.5中可用。[更多信息…numpy.isclose(a,b,rtol=1e-5,atol=1e-8)
:如果和之间的差值小于相对容差wrt和绝对容差之a
和,b
则为真b
。由于相对容差仅计算为wrtb
,因此该测试是不对称的,您可以将其b
视为参考值。支持比较序列由numpy.allclose
。提供。更多信息…unittest.TestCase.assertAlmostEqual(a,b)
:如果a
并且b
在绝对容差范围内,则为真1e-7
。不考虑相对容差,并且不能改变绝对容差,因此该功能不适用于非常大或非常小的数字。此外,它只在子类中可用,unittest.TestCase
并且它很难看,因为它不遵循PEP8。[更多信息…a==pytest.approx(b,rel=1e-6,abs=1e-12)
:如果满足相对容差b
或者满足绝对容差,则为真。由于相对容差仅计算为wrtb
,因此该测试是不对称的,您可以将其b
视为参考值。在明确指定绝对公差而非相对公差的特殊情况下,仅考虑绝对公差。
警告:在3.2版中更改。
为了避免不一致的行为,TypeError
是提高了>
,>=
,<
和<=
比较。以下示例说明了问题:
assert 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10)
assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10)
在第二个例子中,人们期望被调用。但相反,用于比较。这是因为丰富比较的调用层次结构遵循固定行为。
pytest.fail
参考:Skip和xfail:处理无法成功的测试
fail(msg='', pytrace=True): 使用给定消息显式地设置用例为失败状态。
参数:
- msg(str) - 显示用户失败原因的消息。
- pytrace(bool) - 如果为false,则msg表示完整的失败信息,并且不报告任何python回溯。
pytest.skip
skip(msg[, allow_module_level=False]): 使用给定消息跳过测试用例。
应仅在测试(设置,调用或拆除)期间或使用allow_module_level
标志在收集期间调用此函数。此函数也可以在doctests中调用。
参数:
- allow_module_level(bool) - 允许在模块级别调用此函数,跳过模块的其余部分。默认为False。
注意:最好在可能的情况下使用
pytest.mark.skipif
标记来声明在某些条件下跳过的测试,例如不匹配的平台或依赖项。 同样,使用#doctest:+ SKIP
指令(请参阅doctest.SKIP)可以静态地跳过doctest。
pytest.importorskip
importorskip(modname, minversion=None, reason=None): 导入并返回请求的模块modname
,或者如果无法导入模块,则跳过当前测试。
参数:
- modname(str) - 要导入的模块的名称
- minversion(str) - 如果给定,导入的模块
__version__
属性必须至少为此最小版本,否则仍会跳过测试。 - reason(str) - 如果给定,则无法导入模块时,此原因显示为消息。
pytest.xfail
xfail(reason=''): 由于给定的原因,强制标记失败测试用例或测试准备函数。
只能在测试函数, setup函数或teardown函数中使用此函数。
注意:最好在可能的情况下使用
pytest.mark.xfail
标记,在某些条件(如已知错误或缺少的功能)下声明测试是否为xfailed。
pytest.exit
exit(msg, returncode=None): 退出测试过程。
参数:
- msg(str) - 退出时显示的消息。
- returncode(int) - 返回退出pytest时使用的代码。
pytest.main
main(args=None, plugins=None): 执行进程内测试运行后返回退出代码。
参数:
- args- 命令行参数列表。
- plugins- 初始化期间要自动注册的插件对象列表。
pytest.param
param(*values[, id][, marks]): 在[pytest.mark.parametrize指定参数。
@pytest.mark.parametrize("test_input,expected",[
("3+5",8),
pytest.param("6*9",42,marks=pytest.mark.xfail),
])
def test_eval(test_input,expected):
assert eval(test_input) == expected
参数:
- values- 按顺序的参数集值的变量args。
- 标记- 要应用于此参数集的单个标记或标记列表。
- id(str) - 属于此参数集的id。
pytest.raises
参考:关于预期异常的断言。
with raises(expected_exception: Exception[, match][, message]) as excinfo:
断言代码块/函数调用会引发expected_exception
或引发失败异常。
参数:
- match- 如果指定,则断言异常与text或regex匹配
- 消息-(自4.1弃用)如果指定,提供了一种定制的失败消息,如果异常没有升高
使用pytest.raises
的上下文管理器,这将捕获特定类型的异常:
>>> with raises(ZeroDivisionError):
... 1/0
如果代码块没有引发预期的异常(ZeroDivisionError
在上面的示例中),或者根本没有异常,则检查将失败。
你还可以使用keyword参数match
来断言异常与text或regex匹配:
>>> with raises(ValueError,match='must be 0 or None'):
... raise ValueError("value must be 0 or None")
>>> with raises(ValueError,match=r'must be d+/pre>):
... raise ValueError("value must be 42")
上下文管理器生成一个ExceptionInfo
对象,可用于检查捕获的异常的详细信息:
>>> with raises(ValueError) as exc_info:
... raise ValueError("value must be 42")
>>> assert exc_info.type is ValueError
>>> assert exc_info.value.args[0] == "value must be 42"
自4.1版本后不推荐使用:在上下文管理器表单中,您可以使用keyword参数message
指定在pytest.raises
检查失败时将显示的自定义失败消息。这已被弃用,因为它被认为是容易出错的,因为用户通常意味着使用它match
。
注意:当
pytest.raises
用作上下文管理器时,值得注意的是,应用正常的上下文管理器规则,并且引发的异常必须是上下文管理器范围内的最后一行。之后,在上下文管理器范围内的代码行将不会被执行。例如:
value = 15
>>> with raises(ValueError) as exc_info:
... if value > 10:
... raise ValueError("value must be <= 10")
... assert exc_info.type is ValueError # this will not execute
相反,必须采取以下方法(注意范围的差异):
with raises(ValueError) as exc_info:
... if value > 10:
... raise ValueError("value must be <= 10")
...
>>> assert exc_info.type is ValueError
使用pytest.mark.parametrize
使用pytest.mark.parametrize时,可以对测试进行参数化,使得某些运行引发异常,而其他运行则不会。
有关示例,请参阅[参数化条件提升。
遗产形式
可以通过传递一个名为lambda来指定一个可调用的:
raises(ZeroDivisionError,lambda: 1/0)
<ExceptionInfo ...>
或者你可以用参数指定一个任意的callable:
def f(x): return 1/x
...
>>> raises(ZeroDivisionError,f,0)
<ExceptionInfo ...>
>>> raises(ZeroDivisionError,f,x=0)
<ExceptionInfo ...>
上面的表单完全受支持,但不建议使用新代码,因为上下文管理器表单被认为更具可读性且不易出错。
注意:与Python中捕获的异常对象类似,显式清除对返回
ExceptionInfo
对象的本地引用可以帮助Python解释器加速其垃圾回收。
清除这些引用会中断引用循环(ExceptionInfo
- >捕获异常 - >帧堆栈引发异常 - >当前帧堆栈 - >局部变量 - >ExceptionInfo
),这会使Python保留从该循环引用的所有对象(包括当前帧中的所有局部变量) )活着,直到下一个循环垃圾收集运行。有关try
更多详细信息,请参阅官方Python语句文档。
pytest.deprecated_call
参考:确保代码触发弃用警告。
with deprecated_call(): 上下文管理器,可用于确保代码块触发aDeprecationWarning
或PendingDeprecationWarning
:
import warnings
>>> def api_call_v2():
... warnings.warn('use v3 of this api',DeprecationWarning)
... return 200
>>> with deprecated_call():
... assert api_call_v2() == 200
deprecated_call
也可以通过传递函数来使用,*args
并且*kwargs
在这种情况下,它将确保调用产生上面的警告类型之一。func(*args,**kwargs)
pytest.register_assert_rewrite
参考: 断言重写。
register_assert_rewrite(*names): 注册一个或多个要在导入时重写的模块名称。
此函数将确保此模块或程序包内的所有模块将重写其assert语句。因此,您应确保在实际导入模块之前调用此方法,如果您是使用包的插件,则通常在__init__.py中调用。
抛出:TypeError- 如果给定的模块名称不是字符串。
pytest.warns
参考: 使用警告功能发出警告
with warns(expected_warning: Exception[, match]): 断言代码会引发一类特定的警告。
具体来说,参数expected_warning
可以是警告类或警告类序列,并且with
块内部必须发出该类或类的警告。
该助手生成一个warnings.WarningMessage
对象列表,每个警告引发一个对象。
此函数可用作上下文管理器,pytest.raises
也可以使用任何其他方法:
with warns(RuntimeWarning):
... warnings.warn("my warning",RuntimeWarning)
在上下文管理器表单中,您可以使用keyword参数match
来断言异常与text或regex匹配:
with warns(UserWarning,match='must be 0 or None'):
... warnings.warn("value must be 0 or None",UserWarning)
>>> with warns(UserWarning,match=r'must be d+/pre>):
... warnings.warn("value must be 42",UserWarning)
>>> with warns(UserWarning,match=r'must be d+/pre>):
... warnings.warn("this is not here",UserWarning)
Traceback (most recent call last):
...
Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted...
pytest.freeze_includes
参考: 冻结pytest。
freeze_includes(): 返回pytest使用的模块名称列表,应由cx_freeze包含。