pytest官方文档 6.2 中文翻译版(第十二章):skip 和 xfail处理那些无法成功的测试

本文档介绍了pytest中如何使用skip和xfail处理无法成功或预期失败的测试。skipif用于有条件地跳过测试,xfail则标记测试为预期失败。skipif可以根据条件在模块、类或函数级别跳过测试,xfail则允许指定测试失败的原因、条件和是否运行。文章还提到了在参数化测试中如何结合使用skip和xfail。
摘要由CSDN通过智能技术生成

你可以给那些不能在特定平台运行的测试或者那些本就期望失败的用例做个标记,这样ytest就可以处理这种用例并且提供一个测试运行的总结,同时保持测试集 green。

skip的意思是你希望测试在某些情况下可以通过,否则,pytest应该整体跳过这个测试。比较通用的例子是在非windows的环境下跳过那些只能在windows下运行的用例或者在一些资源还没有准备好的时候跳过依赖这些资源的用例(例如一个数据库)。
xfail表示由于一些原因我们期望测试失败。一个常见的例子是我们测试的功能还没有实现或者bug还没有被修复。如果一个测试是期望失败的(被标记为pytest.mark.xfail)但是它通过了,在总结中会报告一个xpass。

pytest对于skip和xfail的个数会分别计算,结果会分别列出。skipped/xfailed的详细信息默认情况下不会显示,以防止输出过于凌乱。你可以使用 -r 选项在测试过程中显示类似于 “short” 选项显示那样的详细信息:

pytest -rxXs # xfailed, xpassed, and skipped 会显示详细的信息

关于-r选项的详细信息可以通过运行 pytest -h 来看到。
(查看 内置的配置文件选项)

12.1 跳过测试方法

最简单的跳过一个测试的方法是使用skip标记函数,还可以传入一个参数作为跳过的原因:

@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown():
	...

另外,我们也可以通过调用 skip(reason) 在准备(setup)或者测试执行过程中命令式的跳过测试:

def test_function():
	if not valid_config():
			pytest.skip("unsupported configuration")

在我们无法在import的时候,确定命令是否执行的情况下,这种命令式的方式十分有用。我们还可以使用pytest.skip(reason, allow_module_level=True)来在模块的层次跳过整个的模块:

import sys
import pytest

if not sys.platform.startswith("win"):
	pytest.skip("skipping windows-only tests", allow_module_level=True)
12.1.1 skipif

如果你希望在某些情况下跳过用例,你就需要使用 skipif 来代替skip。下面是一个例子,它会在系统中python解释器低于3.6的时候跳过用例:

import sys

@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires python3.7 or higher")
def test_function():
	...

如果条件判断为True,则测试函数会被跳过,当使用 -rs的时候,跳过的详细原因会显示在总结中。你可以在模块间共享 skipif,看看下面的模块:

# content of test_mymodule.py
import mymodule

minversion = pytest.mark.skipif(
	mymodule.__versioninfo__ < (1, 1), reason="at least mymodule-1.1 required" 
)

@minversion
def test_function():
	...

你可以在其他模块中调用和复用这个skipif:

# test_myothermodule.py
from test_mymodule import minversion

@minversion
def test_anotherfunction():
	...

在一个很大的测试集中,把那些经常会使用的mark放到一个文件中是一个好的实践。

另外,你也可以使用条件字符串代码布尔类型的值,但是这种方式不容易在模块间共享mark,支持这个主要是处于向后兼容的原因。

12.1.2 跳过一个类或模块中的全部测试

你可以在类上像使用其他标记一样使用 skipif 标记:

@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
class TestPosixCalls:
	def test_function(self):
		"will not be setup or run under 'win32' platform"

如果条件为True,这个标记会导致这个类中的所有的方法被跳过。
如果你想跳过一个模块中的所有测试,你需要全局的使用 pytestmark:

# test_module.py
pytestmark = pytest.mark.skipif(...)

如果一个方法被多个skipif修饰,只要有一个为True,这个测试就会被跳过。

12.1.3 跳过文件和目录

某些情况下你可能需要跳过整个文件或者目录,例如测试依赖了某个Python版本的特定功能或是你不希望pytest运行一些代码。在这种情况下,你必须把文件或者目录从 测试收集 中排除出去。查看 自定义的测试收集 获取更多的信息。
译者注:自定义的测试收集在 27.6.8,通过在ini文件中修改配置来修改pytest收集测试的策略

12.1.4 缺少依赖的时候跳过

你可以在模块级别,测试中,或者测试的setup阶段使用 pytest.importorskip 在特定的依赖不存在的时候跳过测试。

docutils = pytest.importorskip("docutils")

如果 docutils 不能在这里被导入,这会导致测试的跳过。你还可以判断某个库是不是特定的版本来跳过:

docutils = pytest.importorskip("docutils", minversion="0.3")

版本信息会从特定的模块的 version 属性中读取。

12.1.5 总结

这里的例子是说明在一些情况下一个模块中跳过测试:

  1. 无条件跳过模块中所有测试
    pytestmark = pytest.mark.skip("all tests still WIP")
    
  2. 基于条件的跳过模块中的所有测试
    pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="tests for linux only")
    
  3. 在库缺失的情况下跳过测试
    pexpect = pytest.importorskip("pexpect")
    

12.2 XFail: 标记测试为期望失败

你可以使用xfail来指明你希望测试失败:

@pytest.mark.xfail
def test_function():
	...

测试会执行,但是测试失败不会有任何追踪信息。在执行之后,终端窗口会多出两块以显示结果: “expected to fail” (XFAIL) 或者 “unexpectedly passing” (XPASS) 。
另外你也可以使用代码在测试中或者setup中标记测试为xfail。

def test_function():
	if not valid_config():
		pytest.xfail("failing configuration (but should work)")
def test_function2():
	import slow_module
	
	if slow_module.slow_function():
		pytest.xfail("slow_module taking too long")

这两个例子说明了这样一种情况,你无法在模块的级别使用mark来判断条件,也就必须在代码层级使用xfail。
这回导致test_function产生一个XFAIL。注意与mark不同的是, pytest.xfail()之后的代码都不会被执行。这是因为pytest内部是使用一个特定的异常来实现的这个功能。
可以在 pytest.mark.xfail 中看到更多的信息。

12.2.1 condition 参数

如果一个测试只是在某种情况下期望失败,你可以传递一个参数(第一个参数):

@pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library")
def test_function():
	...

注意你必须同时传递reason参数。

12.2.2 reason 参数

你可以使用reason参数描述你期望测试失败的原因:

@pytest.mark.xfail(reason="known parser issue")
def test_function():
	...
12.2.3 raises 参数

如果你想更具体的指定当某一个异常发生的时候,才是期望的失败,你可以指定一个异常或者是使用元组指定一组异常:

@pytest.mark.xfail(raises=RuntimeError)
def test_function():
	...

这样如果测试引发的异常不是我们指定的,就会引发一个普通的失败。

12.2.4 run 参数

如果一个测试应该被标记为 期望失败,报告结果的时候也应该是期望失败,但是这个用例是不应该被执行的,可以将run参数标为false:

@pytest.mark.xfail(run=False)
def test_function():
	...

这对于那些会导致运行崩溃而你又想之后再调试的测试十分有用。

12.2.5 strict 参数

默认情况下XFAIL 和 XPASS都不会导致整个测试的失败。你可以通过将strict参数设置为True来改变这种默认的行为:

@pytest.mark.xfail(strict=True)
def test_function():
	...

这会导致 XPASS(期望失败但是成功了) 结果被当作一个失败的测试。
你也可以再ini文件中,将xfail_strict参数改为True来改变这种默认设置:

[pytest]
xfail_strict=true
12.2.6 忽略 xfail

当我们在命令行中使用下面的参数:

pytest --runxfail

你可以强制标记了xfail的测试按照正常的测试执行和统计结果。这也会导致pytest.xfail()完全没有作用。

12.2.7 Examples

这是一些使用的例子:

import pytest


xfail = pytest.mark.xfail

@xfail
def test_hello():
	assert 0

@xfail(run=False)
def test_hello2():
	assert 0

@xfail("hasattr(os, 'sep')")
def test_hello3():
	assert 0

@xfail(reason="bug 110")
def test_hello4():
	assert 0

@xfail('pytest.__version__[0] != "17"')
def test_hello5():
	assert 0

def test_hello6():
	pytest.xfail("reason")

@xfail(raises=IndexError)
	def test_hello7():
	x = []
	x[1] = 1

加上 report-on-xfail 参数运行之后会给下面的输出:

example $ pytest -rx xfail_demo.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/example
collected 7 items
xfail_demo.py xxxxxxx [100%]
========================= short test summary info ==========================
XFAIL xfail_demo.py::test_hello
XFAIL xfail_demo.py::test_hello2
reason: [NOTRUN]
XFAIL xfail_demo.py::test_hello3
condition: hasattr(os, 'sep')
XFAIL xfail_demo.py::test_hello4
bug 110
XFAIL xfail_demo.py::test_hello5
condition: pytest.__version__[0] != "17"
XFAIL xfail_demo.py::test_hello6
reason: reason
XFAIL xfail_demo.py::test_hello7
============================ 7 xfailed in 0.12s ============================

12.3 Skip/xfail 和参数化

我们可以在将参数化和skip and xfail这样的标记一同使用:

import pytest


@pytest.mark.parametrize(
	("n", "expected"),
	[ 
		(1, 2),
		pytest.param(1, 0, marks=pytest.mark.xfail),
		pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
		(2, 3),
		(3, 4),
		(4, 5),
		pytest.param(
			10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")
		),
	],
)
def test_increment(n, expected):
	assert n + 1 == expected

译者注:我们下一章会讲解参数化的相关知识,可以结合着看这一节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值