10、pytest -- skip和xfail标记

目录

  • 1. 跳过测试用例的执行

    • 1.1. @pytest.mark.skip装饰器
    • 1.2. pytest.skip方法
    • 1.3. @pytest.mark.skipif装饰器
    • 1.4. pytest.importorskip方法
    • 1.5. 跳过测试类
    • 1.6. 跳过测试模块
    • 1.7. 跳过指定文件或目录
    • 1.8. 总结
  • 2. 标记用例为预期失败的
    • 2.1. 去使能xfail标记
  • 3. 结合pytest.param方法

往期索引:https://www.cnblogs.com/luizyao/p/11771740.html

实际工作中,测试用例的执行可能会依赖于一些外部条件,例如:只能运行在某个特定的操作系统(Windows),或者我们本身期望它们测试失败,例如:被某个已知的Bug所阻塞;如果我们能为这些用例提前打上标记,那么pytest就相应地预处理它们,并提供一个更加准确的测试报告;

在这种场景下,常用的标记有:

  • skip:只有当某些条件得到满足时,才执行测试用例,否则跳过整个测试用例的执行;例如,在非Windows平台上跳过只支持Windows系统的用例;
  • xfail:因为一个确切的原因,我们知道这个用例会失败;例如,对某个未实现的功能的测试,或者阻塞于某个已知Bug的测试;

pytest默认不显示skipxfail用例的详细信息,我们可以通过-r选项来自定义这种行为;

通常,我们使用一个字母作为一种类型的代表,具体的规则如下:

(f)ailed, (E)rror, (s)kipped, (x)failed, (X)passed, (p)assed, (P)assed with output, (a)ll except passed(p/P), or (A)ll

例如,显示结果为XFAILXPASSSKIPPED的用例:

pytest -rxXs

更多细节可以参考:2、使用和调用 -- 总结报告

1. 跳过测试用例的执行

1.1. @pytest.mark.skip装饰器

跳过执行某个用例最简单的方式就是使用@pytest.mark.skip装饰器,并且可以设置一个可选参数reason,表明跳过的原因;

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

1.2. pytest.skip方法

如果我们想在测试执行期间(也可以在SetUp/TearDown期间)强制跳过后续的步骤,可以考虑pytest.skip()方法,它同样可以设置一个参数msg,表明跳过的原因;

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

另外,我们还可以为其设置一个布尔型的参数allow_module_level(默认是False),表明是否允许在模块中调用这个方法,如果置为True,则跳过模块中剩余的部分;

例如,在Windows平台下,不测试这个模块:

import sys
import pytest

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

注意:

当在用例中设置allow_module_level参数时,并不会生效;

def test_one():
    pytest.skip("跳出", allow_module_level=True)

def test_two():
    assert 1

也就是说,在上述示例中,并不会跳过test_two用例;

1.3. @pytest.mark.skipif装饰器

如果我们想有条件的跳过某些测试用例的执行,可以使用@pytest.mark.skipif装饰器;

例如,当python的版本小于3.6时,跳过用例:

import sys

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

我们也可以在两个模块之间共享pytest.mark.skipif标记;

例如,我们在test_module.py中定义了minversion,表明当python的最低支持版本:

# src/chapter-10/test_module.py

import sys

import pytest

minversion = pytest.mark.skipif(sys.version_info < (3, 8),
                                reason='请使用 python 3.8 或者更高的版本。')

@minversion
def test_one():
    assert True

并且,在test_other_module.py中引入了minversion

# src/chapter-10/test_other_module.py

from test_module import minversion

@minversion
def test_two():
    assert True

现在,我们来执行这两个用例(当前虚拟环境的python版本为3.7.3):

λ pipenv run pytest -rs -k 'module' src/chapter-10/
================================ test session starts =================================
platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
rootdir: D:\Personal Files\Projects\pytest-chinese-doc
collected 2 items

src\chapter-10\test_module.py s                                                 [ 50%]
src\chapter-10\test_other_module.py s                                           [100%]

============================== short test summary info ===============================
SKIPPED [1] src\chapter-10\test_module.py:29: 请使用 python 3.8 或者更高的版本。
SKIPPED [1] src\chapter-10\test_other_module.py:26: 请使用 python 3.8 或者更高的版本。
================================= 2 skipped in 0.03s =================================

可以看到,minversion在两个测试模块中都生效了;

因此,在大型的测试项目中,可以在一个文件中定义所有的执行条件,需要时再引入到模块中;

另外,需要注意的是,当一个用例指定了多个skipif条件时,只需满足其中一个,就可以跳过这个用例的执行;

注意:不存在pytest.skipif()的方法;

1.4. pytest.importorskip方法

当引入某个模块失败时,我们同样可以跳过后续部分的执行;

docutils = pytest.importorskip("docutils")

我们也可以为其指定一个最低满足要求的版本,判断的依据是检查引入模块的__version__属性:

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

我们还可以再为其指定一个reason参数,表明跳过的原因;

我们注意到pytest.importorskippytest.skip(allow_module_level=True)都可以在模块的引入阶段跳过剩余部分;实际上,在源码中它们抛出的都是同样的异常:

# pytest.skip(allow_module_level=True)

raise Skipped(msg=msg, allow_module_level=allow_module_level)
# pytest.importorskip()

raise Skipped(reason, allow_module_level=True) from None

只是importorskip额外增加了minversion参数:

# _pytest/outcomes.py

if minversion is None:
        return mod
    verattr = getattr(mod, "__version__", None)
    if minversion is not None:
        if verattr is None or Version(verattr) < Version(minversion):
            raise Skipped(
                "module %r has __version__ %r, required is: %r"
                % (modname, verattr, minversion),
                allow_module_level=True,
            )

从中我们也证实了,它实际检查的是模块的__version__属性;

所以,对于一般场景下,使用下面的方法可以实现同样的效果:

try:
    import docutils
except ImportError:
    pytest.skip("could not import 'docutils': No module named 'docutils'",
                allow_module_level=True)

1.5. 跳过测试类

在类上应用@pytest.mark.skip@pytest.mark.skipif

# src/chapter-10/test_skip_class.py

import pytest

@pytest.mark.skip("作用于类中的每一个用例,所以 pytest 共收集到两个 SKIPPED 的用例。")
class TestMyClass():
    def test_one(self):
        assert True

    def test_two(self):
        assert True

1.6. 跳过测试模块

在模块中定义pytestmark变量(推荐):

# src/chapter-10/test_skip_module.py

import pytest

pytestmark = pytest.mark.skip('作用于模块中的每一个用例,所以 pytest 共收集到两个 SKIPPED 的用例。')

def test_one():
    assert True

def test_two():
    assert True

或者,在模块中调用pytest.skip方法,并设置allow_module_level=True

# src/chapter-10/test_skip_module.py

import pytest

pytest.skip('在用例收集阶段就已经跳出了,所以不会收集到任何用例。', allow_module_level=True)

def test_one():
    assert True

def test_two():
    assert True

1.7. 跳过指定文件或目录

通过在conftest.py中配置collect_ignore_glob项,可以在用例的收集阶段跳过指定的文件和目录;

例如,跳过当前测试目录中文件名匹配test_*.py规则的文件和config的子文件夹sub中的文件:

collect_ignore_glob = ['test*.py', 'config/sub']

更多细节可以参考:https://docs.pytest.org/en/5.1.3/example/pythoncollection.html#customizing-test-collection

1.8. 总结

pytest.mark.skip pytest.mark.skipif pytest.skip pytest.importorskip conftest.py
用例 @pytest.mark.skip() @pytest.mark.skipif() pytest.skip(msg=‘‘) / /
@pytest.mark.skip() @pytest.mark.skipif() / / /
模块 pytestmark = pytest.mark.skip() pytestmark = pytest.mark.skipif() pytest.skip(allow_module_level=True) pytestmark = pytest.importorskip() /
文件或目录 / / / / collect_ignore_glob

2. 标记用例为预期失败的

我们可以使用@pytest.mark.xfail标记用例,表示期望这个用例执行失败;

用例会正常执行,只是失败时不再显示堆栈信息,最终的结果有两个:用例执行失败时(XFAIL:符合预期的失败)、用例执行成功时(XPASS:不符合预期的成功)

另外,我们也可以通过pytest.xfail方法在用例执行过程中直接标记用例结果为XFAIL,并跳过剩余的部分:

def test_function():
    if not valid_config():
        pytest.xfail("failing configuration (but should work)")

同样可以为pytest.xfail指定一个reason参数,表明原因;

下面我们来重点看一下@pytest.mark.xfail的用法:

  • condition位置参数,默认值为None

    @pytest.mark.skipif一样,它也可以接收一个python表达式,表明只有满足条件时才标记用例;

    例如,只在pytest 3.6版本以上标记用例:

    @pytest.mark.xfail(sys.version_info >= (3, 6), reason="python3.6 api changes")
    def test_function():
        ...
  • reason关键字参数,默认值为None

    可以指定一个字符串,表明标记用例的原因;

  • strict关键字参数,默认值为False

    strict=False时,如果用例执行失败,结果标记为XFAIL,表示符合预期的失败;如果用例执行成功,结果标记为XPASS,表示不符合预期的成功;

    strict=True时,如果用例执行成功,结果将标记为FAILED,而不再是XPASS了;

    我们也可以在pytest.ini文件中配置:

    [pytest]
    xfail_strict=true
  • raises关键字参数,默认值为None

    可以指定为一个异常类或者多个异常类的元组,表明我们期望用例上报指定的异常;

    如果用例的失败不是因为所期望的异常导致的,pytest将会把测试结果标记为FAILED;

  • run关键字参数,默认值为True:

    run=False时,pytest不会再执行测试用例,直接将结果标记为XFAIL

我们以下表来总结不同参数组合对测试结果的影响(其中xfail = pytest.mark.xfail):

@xfail() @xfail(strict=True) @xfail(raises=IndexError) @xfail(strict=True, raises=IndexError) @xfail(..., run=False)
用例测试成功 XPASS FAILED XPASS FAILED XFAIL
用例测试失败,上报AssertionError XFAIL XFAIL FAILED FAILED XFAIL
用例上报IndexError XFAIL XFAIL XFAIL XFAIL XFAIL

2.1. 去使能xfail标记

我们可以通过命令行选项pytest --runxfail来去使能xfail标记,使这些用例变成正常执行的用例,仿佛没有被标记过一样:

同样,pytest.xfail()方法也将会失效;

3. 结合pytest.param方法

pytest.param方法可用于为@pytest.mark.parametrize或者参数化的fixture指定一个具体的实参,它有一个关键字参数marks,可以接收一个或一组标记,用于标记这轮测试的用例;

我们以下面的例子来说明:

# src/chapter-10/test_params.py

import pytest
import sys

@pytest.mark.parametrize(
    ('n', 'expected'),
    [(2, 1),
     pytest.param(2, 1, marks=pytest.mark.xfail(), id='XPASS'),
     pytest.param(0, 1, marks=pytest.mark.xfail(raises=ZeroDivisionError), id='XFAIL'),
     pytest.param(1, 2, marks=pytest.mark.skip(reason='无效的参数,跳过执行')),
     pytest.param(1, 2, marks=pytest.mark.skipif(sys.version_info <= (3, 8), reason='请使用3.8及以上版本的python。'))])
def test_params(n, expected):
    assert 2 / n == expected

执行:

λ pipenv run pytest -rA src/chapter-10/test_params.py
================================ test session starts =================================
platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
rootdir: D:\Personal Files\Projects\pytest-chinese-doc
collected 5 items

src\chapter-10\test_params.py .Xxss                                             [100%]

======================================= PASSES =======================================
============================== short test summary info ===============================
PASSED src/chapter-10/test_params.py::test_params[2-1]
SKIPPED [1] src\chapter-10\test_params.py:26: 无效的参数,跳过执行
SKIPPED [1] src\chapter-10\test_params.py:26: 请使用3.8及以上版本的python。
XFAIL src/chapter-10/test_params.py::test_params[XFAIL]
XPASS src/chapter-10/test_params.py::test_params[XPASS]
================= 1 passed, 2 skipped, 1 xfailed, 1 xpassed in 0.08s =================

关于参数化的fixture的细节可以参考:4、fixtures:明确的、模块化的和可扩展的 -- 在参数化的fixture中标记用例

原文地址:https://www.cnblogs.com/luizyao/p/11806037.html

时间: 2024-09-30 11:40:55

10、pytest -- skip和xfail标记的相关文章

pytest 13 使用自定义标记mark

前言: pytest可以规定那些要跑,那些不跑,跑特定的哪些?比如以下的这个例子: #!/usr/bin/env/python # -*-coding:utf-8-*- import pytest @pytest.mark.runtest def test_run(): print("run") def test_not_run(): pass def test_not_run1(): pass class TestClass: def test_method(self): pass

pytest文档31-allure标记用例级别severity

前言 我们在做功能测试的时候,执行完一轮测试用例,输出测试报告的时候,会有统计缺陷的数量和等级. 在做自动化测试的过程中,当你的测试用例越来越多的时候,如果执行一轮测试发现了几个测试不通过,我们也希望能快速统计出缺陷的等级. pytest结合allure框架可以对用例的等级做详细的划分. 用例等级 allure对用例的等级划分成五个等级 blocker 阻塞缺陷(功能未实现,无法下一步) critical 严重缺陷(功能点缺失) normal 一般缺陷(边界情况,格式错误) minor 次要缺陷

pytest文档12-skip跳过用例

前言 pytest.mark.skip可以标记无法在某些平台上运行的测试功能,或者您希望失败的测试功能 skip意味着只有在满足某些条件时才希望测试通过,否则pytest应该跳过运行测试. 常见示例是在非Windows平台上跳过仅限Windows的测试,或跳过测试依赖于当前不可用的外部资源(例如数据库). xfail意味着您希望测试由于某种原因而失败. 一个常见的例子是对功能的测试尚未实施,或尚未修复的错误. 当测试通过时尽管预计会失败(标有pytest.mark.xfail),它是一个xpas

用 pytest 测试 python 代码

Pytest 是一个比较成熟且功能完备的 Python 测试框架.其提供完善的在线文档,并有着大量的第三方插件和内置帮助,适用于许多小型或大型项目.Pytest 灵活易学,打印调试和测试执行期间可以捕获标准输出,适合简单的单元测试到复杂的功能测试.还可以执行 nose, unittest 和 doctest 风格的测试用例,甚至 Django 和 trial.支持良好的集成实践, 支持扩展的 xUnit 风格 setup,支持非 python 测试.支持生成测试覆盖率报告,支持 PEP8 兼容的

pytest测试框架介绍(3)

12.fixture带参数传递 场景:测试离不开数据,为了数据灵活,一般数据都是通过参数传的 解决:fixture通过固定参数request传递: 步骤:在fixture中增加@pytest.fixture(params=[1,2,3,'linda'])在方法参数写request 如下图,运行结果: 参数传入的可以是列表是元祖 如下图,eval将字符串str当成有效的表达式来求值,并返回结果: 当我们测试登录或者搜索这种同样的场景需要不同的数据时,就可以使用这种参数组合的方法: 运行结果如下:

pytest-15-自定义标记mark

pytest可以支持自定义标记,自定义标记可以把一个web项目划分多个模块,然后指定模块名称执行.app自动化的时候,如果想android和ios公用一套代码时,也可以使用标记功能,标明哪些是ios用例,哪些是android的,运行代码时候指定mark名称运行就可以 mark标记 1.以下用例,标记test_send_http()为webtest # content of test_server.py import pytest @pytest.mark.webtest def test_sen

跳过用例skip

1.装饰器,放在函数前面,跳过用例 @pytest.mark.skip(reason="no way of currently testing this") import pytest def test1(): print('操作1') print("-----------------------------------------------") @pytest.mark.skip(reason="no way of currently testing

试试 IEnumerable 的 10 个小例子

IEnumerable 接口是 C# 开发过程中非常重要的接口,对于其特性和用法的了解是十分必要的.本文将通过10个小例子,来熟悉一下其简单的用法. 全是源码 以下便是这10个小例子,响应的说明均标记在注释中. 每个以 TXX 开头命名的均是一个示例.建议从上往下阅读. using System; using System.Collections.Generic; using System.Configuration; using System.IO; using System.Linq; us

汇编语言标记寄存器标记位_NV UP EI NG NZ AC PE CY

在8086CPU中,有一种标记寄存器,长度为16bit: 其中存储的信息被称为程序状态字(Program Status Word,PSW),以下将该寄存器简称为flag. 功能:1)用来存储相关指令的某些执行结果: 2)用来为CPU执行相关指令提供行为依据: 3)用来控制CPU的相关工作方式. flag寄存器各位示意图 注明:16位中未标明字符的各位在8086CPU中没有使用,无意义: 标明字符的各位具有特殊含义. 位     符号    英文全称