pytest

成熟的全功能Python测试框架

  • 简略灵便,容易上手
  • 反对参数化
  • 测试用例的skip与xfail,主动失败重试等解决
  • 可能反对简略的单元测试和简单的功能测试,还能够用来做selenium/appium等自动化测试、接口自动化测试(pytest+requests)
  • 具备很多第三方插件,并能够自定义扩大:pytest-allure,pytest-xdist(多CPU开发)等
  • 反对Jenkins集成

Pytest 使用手册

pytest 库装置:

pip install pytest 

测试用例的辨认与运行

  1. 对于文件的命名要求
    • test_*.py
    • *_test.py
  1. 对于用例的命名要求:
    • Test 类蕴含的所有 test_ 的办法(测试类不能带有 _ init_  办法)
    • 不在 class 中的所有的 test_* 办法

pytest 能够执行 unittest 框架些的用例和办法

pycharm 应用 pytest 框架时,须要指定,具体操作如图所示:

命令行运行形式:

# 间接执行以后所在门路下辨认到的测试用例pytest# 执行指定文件pytest test_material_master.py# 执行时打印日志pytest -v

复原应用 Python 解释器运行的形式:

此时的运行形式为:

import pytestif __name__ == '__main__':    pytest.main(["test_material_master.py"])  # 运行整个文件的用例      pytest.main(["test_material_master.py::TestMaterialMaster::test_mat_search_by_matCode"])  # 运行文件中指定的某条的用例

参数化应用

应用办法:

# 作为装璜器@pytest.mark.parametrize(argnames, argvalues)# argnames:要参数化的变量;类型:能够是 str(应用逗号宰割),list,tuple# argvalues:参数化的值;类型:list,[tuple]

理论利用:

@pytest.mark.parametrize("a, b, expect",                         get_data(yaml_path, "add"),                         ids=["整数", "小数", "大整数"])def test_add(self, a, b, expect, setup_fixture):    """测试加法正向用例"""    result = setup_fixture.add_func(a, b)    assert abs(result - expect) < 0.01

注意事项:

参数组合(笛卡尔积):实用于只有一个冀望后果的状况

import pytest@pytest.mark.parametrize("a", [1, 2, 3])@pytest.mark.parametrize("b", [4, 5, 6])def test_add(a, b):    print("参数组合 a = {}, b = {}".format(a, b))    执行后果:PASSED                                            [ 11%]参数组合 a = 1, b = 4PASSED                                            [ 22%]参数组合 a = 2, b = 4PASSED                                            [ 33%]参数组合 a = 3, b = 4PASSED                                            [ 44%]参数组合 a = 1, b = 5PASSED                                            [ 55%]参数组合 a = 2, b = 5PASSED                                            [ 66%]参数组合 a = 3, b = 5PASSED                                            [ 77%]参数组合 a = 1, b = 6PASSED                                            [ 88%]参数组合 a = 2, b = 6PASSED                                            [100%]参数组合 a = 3, b = 6

skip

应用场景:写测试用例时,发现某个用例自身就存在 bug,而且临时无奈修复,就能够先跳过它

import pytest@pytest.mark.skip("存在bug,先跳过")@pytest.mark.parametrize("a", [1, 2, 3])@pytest.mark.parametrize("b", [4, 5, 6])def test_add(a, b):    print("参数组合 a = {}, b = {}".format(a, b))

mark

应用场景: 对用例进行分类,贴标签

import pytest@pytest.mark.test1def test_01():    print("标记为冒烟测试用例")    @pytest.mark.test2def test_02():    print("标记为回归测试用例")    # 只执行标记了 test1 的用例,命令行:# pytest -s test.py -m test1# 反选 pytest -s test.py -m "not test1"

前置与后置

用例运行级别

  • 模块级:开始与模块始末,全局无效; setup_module / teardown_module 
  • 函数级:只对函数用例失效(不在类中应用); setup_function / teardown_function 
  • 类级:只在类的前后运行一次(在类中应用); setup_class / teardown_class 
  • 办法级:开始与办法始末(在类中应用); setup_method / teardown_method 
  • 类中应用,运行在调用办法的前后; setup / teardown  最为罕用**

fixture:自定义前置/后置

@pytest.fixture() 的劣势:

  • 命名形式灵便,不局限于setup / teardown
  • 通过 conftest.py 配置能够实现数据共享,不须要 import 就能自动识别
  • scope="module" 能够实现多个 .py 跨文件共享前置,每个 .py 文件调用一次
  • scope="session" 能够实现多个 .py 跨文件应用一个 session 来实现多个用例
import pytest@pytest.fixture(scope="function")  # 通过配置装璜器定义def login_fixture():    """登陆前置"""    print("提前登陆")def test_01(login_fixture):  # login_fixture 作为参数传入    print("测试用例 01")def test_02():    print("测试用例 02")    执行后果:test.py::test_01 提前登陆 PASSED                                         [ 50%]测试用例 01test.py::test_02 PASSED                                                  [100%]测试用例 02

fixture 参数

scope:作用范畴

  • function:每个test都运行,默认是function的scope
  • class:每个class的所有test只运行一次
  • module:每个module的所有test只运行一次
  • session:每个session只运行一次

autouse: 审慎应用

  • 默认为 False
  • 当为 True,每个测试用例会主动调用该 fixture,无需传入 fixture 函数名

fixture中应用参数

fixture函数能够参数化,在这种状况下,它们将被屡次调用,每次执行一组相干测试,即依赖于这个fixture的测试,测试函数通常不须要晓得它们的从新运行

import pytest@pytest.fixture(params=["参数1","参数2"])def myfixture(request):    print("执行testPytest里的前置函数,%s" % request.param)

fixture中返回参数

import pytest@pytest.fixture(params=["参数1","参数2"])def myfixture(request):    return request.paramdef test_print_param(myfixture):    print("执行test_two")    print(myfixture)    assert 1==1    # 输入PASSED                    [ 50%]执行test_two 参数1PASSED                    [100%]执行test_two参数2

conftest.py

利用场景:

  • 每个接口需共用到的token
  • 每个接口需共用到的测试用例数据
  • 每个接口需共用到的配置信息

应用的注意事项:

  • 文件名 conftest.py 是固定的,不能批改为其余文件名
  • conftest.py 文件与运行的用例要在同一个 pakage 下,并且有 __init__.py 文件
  • 不须要通过 import 导入,pytest 的用例会自动识别
  • 所有同目录测试文件运行前都会执行conftest.py文件
# conftest.py 文件import pytest@pytest.fixture()def login():    print("登陆前置")

后置操作-yield

@pytest.fixture(scope="function")def demo_fix():    print("测试用例的前置筹备操作")    yield    print("测试用例的后置操作")def test_1(demo_fix):    print("开始执行测试用例1")def test_2():    print("开始执行测试用例2")    def test_3(demo_fix):    print("开始执行测试用例3")

如果测试用例中的代码出现异常或者断言失败,并不会影响他的固件中 yield 后的代码执行;

然而如果 fixture 中的 yield 之前的代码也就是相当于setup局部的带代码, 呈现谬误或断言失败,那么 yield 后的代码将不会再执行, 当然测试用例中的代码也不会执行

终结函数-addfinalizer

相当于 try...except 中的 finally

即便 setup 呈现问题了,addfinalizer 仍会执行 teardown 的操作

@pytest.fixture(scope="session")def login_xadmin_fix(request):    s = requests.session()    login_xadmin(s)    def close_s():        s.close()   # 敞开s  用例实现后最初的清理        request.addfinalizer(close_s)        return s

罕用参数阐明

  • -v:能够输入用例执行的详细信息;如用例坐在的文件及用例名称
  • -s:输出用例的调试信息;如 print 的打印信息
  • -x:遇到失败的用例时立刻进行
  • -maxfail:用例失败达到肯定数量时,进行运行; pytest -maxfail=num 
  • -m:运行含有 @pytest.mark.标记名  的测试用例
  • -k:执行合乎匹配的测试用例测试;如 pytest -k "raises and not delete"  运行所有蕴含 raises 但不蕴含 delete 的测试

pytest实用插件介绍

pytest-rerunfailures: 用例失败后主动从新运行

装置办法:

pip install pytest-rerunfailures

应用办法:

pytest test_x.py --reruns=n  #失败后重运行的次数

同时也能够在脚本中指定定义重跑的次数,这个时候在运行的时候,就无需加上 --reruns 这个参数

@pytest.mark.flaky(reruns=6, reruns_delay=2)def test_example(self):    print(3)    assert random.choice([True, False])

pytest-assume:多重校验

pytest中的 python 的 assert 断言,也能够写多个断言,但一个失败,前面的断言将不再执行

而 pytest-assume ,即便后面的断言失败了,后续的断言也会继续执行

装置办法:

pip install pytest-assume

应用办法:

def test_simple_assume(x, y):    pytest.assume(x == y)    pytest.assume(True)    pytest.assume(False)

pytest-xdist:分布式并发执行

pytest-xdist 能够让自动化测试用例能够分布式执行,从而节俭自动化测试工夫

分布式执行用例的设计准则:

  1. 用例之间是独立的,用例之间没有依赖关系,用例能够齐全独立运行【独立运行】
  2. 用例执行没有程序,随机程序都能失常执行【随机执行】
  3. 每个用例都能反复运行,运行后果不会影响其余用例【不影响其余用例】

装置办法:

pip install pytest-xdist

应用办法:

多 CPU 并行执行用例,间接加个 -n 参数即可,前面 num 参数就是并行数量,比方 num 设置为 3

pytest -n 3

pytest-ordering:管制用例的执行程序

装置办法:

pip install pytest-ordering

应用办法:

import pytest@pytest.mark.run(order=2)def test_foo():    assert True@pytest.mark.run(order=1)def test_bar():    assert True

PS: 尽量不要让测试用例有程序,尽量不要让测试用例有依赖!

hook(钩子)函数定制和扩大插件

Pytest在收集完所有测试用例后调用该钩子办法。咱们能够定制化性能实现:

  1. 自定义用例执行程序
  2. 解决编码问题(中文测试用例名称)
  3. 主动增加标签

倡议将 hook 函数的代码写在 conftest.py 文件中

# conftest.pyfrom typing import Listdef pytest_collection_modifyitems(    session: "Session", config: "Config", items: List["Item"]) -> None:    for item in items:        item.name = item.name.encode("utf-8").decode("unicode-escape")        item._nodeid = item.nodeid.encode("utf-8").decode("unicode-escape")