08-pytest常用插件

常用插件

  • pip install pytest-rerunfailures 失败重跑
  • pip install pytest-assume 多重校验
  • pip install pytest-ordering 控制用例的执行顺序
  • pip install pytest-repeat 重复执行用例

pytest-rerunfailures

  • 场景
    • 测试失败后要重新运行n次,要在重新运行之间添加延迟时间、间隔n秒再运行。
  • 安装
  • 运行要求
    • Python 3.6~3.9, or PyPy3
    • pytest 5.0+
  • 使用方式
    • 命令行参数:--reruns n(重新运行次数),--reruns-delay m(等待运行秒数)
    • 装饰器参数:reruns=n(重新运行次数),reruns_delay=m(等待运行秒数)

重新运行所有失败的用例

要重新运行所有测试失败,使用 --reruns 命令行选项,并指定要运行测试的最大次数:
pytest -vs --reruns 3 test_rerun.py

from time import sleep


def test_rerun1():
    sleep(0.5)
    assert 1 == 2

def test_rerun2():
    sleep(0.5)
    assert 2 == 2

def test_rerun2():
    sleep(0.5)
    assert 'h' == 'world'

执行结果:

test_rerun.py::test_rerun1 RERUN
test_rerun.py::test_rerun1 RERUN
test_rerun.py::test_rerun1 RERUN
test_rerun.py::test_rerun1 FAILED
test_rerun.py::test_rerun2 RERUN
test_rerun.py::test_rerun2 RERUN
test_rerun.py::test_rerun2 RERUN
test_rerun.py::test_rerun2 FAILED

添加重新运行的延时

要在两次重试之间增加延迟时间,使用 --reruns-delay 命令行选项,指定下次测试重新开始之前等待的秒数
pytest test_rerun.py --reruns 3 --reruns-delay 1
说明:明显看到重跑的两个测试用例之间有一段时间间隔

重新运行指定的测试用例

@pytest.mark.flaky(reruns=2,reruns_delay=2)
运行结果:

test_rerun.py::test_rerun1 FAILED
test_rerun.py::test_rerun2 PASSED
test_rerun.py::test_rerun3 RERUN
test_rerun.py::test_rerun3 RERUN
test_rerun.py::test_rerun3 FAILED

兼容性问题

  • 不可以和fixture装饰器一起使用: @pytest.fixture()
  • 该插件与pytest-xdist的 --looponfail 标志不兼容
  • 该插件与核心--pdb标志不兼容
    最后总结,这个插件虽然还用,但是坑还是不少,建议主要使用失败重试次数和重试间隔的功能即可。

pytest-assume

  • 场景
    • 一个方法中些多条断言,中间有一条失败,后面的代码就不执行了,我们希望有失败也能执行完毕。
  • 安装
  • 使用方式
    • pytest.assume(1==4)
    • pytest.assume(2==4)

assert 多重断言

def test_assume():
    assert 1 == 2
    assert False == True
    assert 100 == 200

运行结果:

FAILED
test_assume.py:8 (test_assume)
1 != 2

Expected :2
Actual   :1
<Click to see difference>

def test_assume():
>       assert 1 == 2
E       assert 1 == 2

test_assume.py:10: AssertionError

结论
可以看到,第一行断言失败之后,后面的断言也不会执行,包括正常的代码

pytest.assume多重断言

def test_assume():
    # assert 1 == 2
    # assert False == True
    # assert 100 == 200
    pytest.assume(1 == 1)
    pytest.assume(False == True)
    pytest.assume(100 == 200)
    pytest.assume(4 != 1)

运行结果:

FAILED
test_assume.py:10 (test_assume)
tp = <class 'pytest_assume.plugin.FailedAssumption'>, value = None, tb = None

    def reraise(tp, value, tb=None):
        try:
            if value is None:
                value = tp()
            if value.__traceback__ is not tb:
>               raise value.with_traceback(tb)
E               pytest_assume.plugin.FailedAssumption: 
E               2 Failed Assumptions:
E               
E               test_assume.py:16: AssumptionFailure
E               >>	pytest.assume(False == True)
E               AssertionError: assert False
E               
E               test_assume.py:17: AssumptionFailure
E               >>	pytest.assume(100 == 200)
E               AssertionError: assert False

结论

  • 可以看到,第二行即使断言失败,后面的断言还是会继续执行
  • 这有助于我们分析和查看到底一共有哪些断言是失败的
  • 而且最后的代码也还会正常执行,比直接用assert更高效

pytest-ordering

  • 场景
    • 对于集成测试,通常会有上下文依赖关系的测试用例。有的需求,先执行某些需求。测试用例尽量不要有依赖。
  • 安装
  • 使用方式
    • @pytest.mark.run(order=2)

pytest中测试用例执行顺序是从上往下执行

import pytest

def test_1():
    assert True

def test_2():
    assert 1 == 1

def test_2():
    assert 2 == 2

运行结果:
08-pytest常用插件

pytest-ordering控制执行顺序

import pytest


@pytest.mark.run(order=3)
def test_1():
    assert True

@pytest.mark.run(order=1)
def test_2():
    assert 1 == 1

@pytest.mark.run(order=2)
def test_3():
    assert 2 == 2

执行结果:
08-pytest常用插件

总结:
1、除了用@pytest.mark.run(order=3)这种方式指定测试用例顺序
2、还可以使用pytest.mark.third 方法指定

pytest-repeat

  • 场景
    • 平常在做功能测试的时候,经常会遇到某个模块不稳定,偶然会出现一些bug,对于这种问题我们会针对此用例反复执行多次,最终复现出问题来
    • 自动化运行用例时候,也会出现偶然的bug,可以针对单个用例,或者针对某个模块的用例重复执行多次
  • 安装
  • 运行要求
    • Python 2.7、3.4+或PyPy
    • py.test 2.8或更高版本* 使用方式
  • 使用方式
    • count=2
    • count 2
    • pytest --html=report.html --self-contained-html -s --reruns=5 --count=2 test_request.py

重复测试直到失败(重要!!!)

- 如果需要验证偶现问题,可以一次又一次地运行相同的测试直到失败,这个插件将很有用
- 可以将pytest的 -x 选项与pytest-repeat结合使用,以强制测试运行程序在第一次失败时停止

pytest --count=1000 -x test_file.py

代码如下:

def test_example():
    import random
    flag = random.choice([True, False])
    print(flag)
    sleep(1)
    assert flag

执行命令:
pytest --count 5 -x test_repeat.py

执行结果:

test_repeat.py::test_example[1-5] True
PASSED
test_repeat.py::test_example[2-5] True
PASSED
test_repeat.py::test_example[3-5] False
FAILED

================================================================= FAILURES =================================================================
____________________________________________________________ test_example[3-5] _____________________________________________________________

    def test_example():
        import random
        flag = random.choice([True, False])
        print(flag)
        sleep(1)
>       assert flag
E       assert False

test_repeat.py:17: AssertionError
========================================================= short test summary info ==========================================================
FAILED test_repeat.py::test_example[3-5] - assert False
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
======================================================= 1 failed, 2 passed in 3.10s ========================================================

总结:执行了三次,第三次失败,就不再执行了。

@pytest.mark.repeat(count)

如果要在代码中将某些测试用例标记为执行重复多次,可以使用 @pytest.mark.repeat(count)

@pytest.mark.repeat(5)
def test_repeat():
    sleep(1)
    print("测试用例执行")

执行命令:
pytest test_repeat.py::test_repeat

执行结果:

test_repeat.py::test_repeat[1-5] 测试用例执行
PASSED
test_repeat.py::test_repeat[2-5] 测试用例执行
PASSED
test_repeat.py::test_repeat[3-5] 测试用例执行
PASSED
test_repeat.py::test_repeat[4-5] 测试用例执行
PASSED
test_repeat.py::test_repeat[5-5] 测试用例执行
PASSED

============================================================ 5 passed in 5.05s =============================================================

--repeat-scope

命令行参数

作用:可以覆盖默认的测试用例执行顺序,类似fixture的scope参数

- function:默认,范围针对每个用例重复执行,再执行下一个用例
- class:以class为用例集合单位,重复执行class里面的用例,再执行下一个
- module:以模块为单位,重复执行模块里面的用例,再执行下一个
- session:重复整个测试会话,即所有测试用例的执行一次,然后再执行第二次

class示例

class TestRepeat:

    def test_repeat1(self):
        print("test1++++++++test1")

    def test_repeat2(self):
        print("test2+++++++++test2")


class TestRepeat2:

    def test_repeat3(self):
        print("test3++++++++test3")

    def test_repeat4(self):
        print("test4+++++++++test4")

执行命令: pytest --count=2 --repeat-scope=class test_repeat.py
执行结果:

test_repeat.py::TestRepeat::test_repeat1[1-2] test1++++++++test1
PASSED
test_repeat.py::TestRepeat::test_repeat2[1-2] test2+++++++++test2
PASSED
test_repeat.py::TestRepeat::test_repeat1[2-2] test1++++++++test1
PASSED
test_repeat.py::TestRepeat::test_repeat2[2-2] test2+++++++++test2
PASSED
test_repeat.py::TestRepeat2::test_repeat3[1-2] test3++++++++test3
PASSED
test_repeat.py::TestRepeat2::test_repeat4[1-2] test4+++++++++test4
PASSED
test_repeat.py::TestRepeat2::test_repeat3[2-2] test3++++++++test3
PASSED
test_repeat.py::TestRepeat2::test_repeat4[2-2] test4+++++++++test4
PASSED

============================================================ 8 passed in 0.04s =============================================================

兼容性问题

  • pytest-repeat不能与unittest.TestCase测试类一起使用。
  • 无论--count设置多少,这些测试始终仅运行一次,并显示警告

其他

后续单独讲解pytest-xdis(重要!!!)插件的使用。

上一篇:01.Mapreduce实例——去重


下一篇:Pytest系列(15)- 多重校验插件之pytest-assume的详细使用