• Pytest框架教程


    Pytest框架教程

    本文参考了官方文档和一些乐于分享的大佬的博客,结合自己的理解完成。学习pytest框架的小白,需要按照教程自己敲一遍,配置一遍,摸索一下整个框架的运行逻辑,数据流的走向,文字虽多,请细细看完,有问题欢迎在群里提出,相互学习,互相指正。希望大家有所收获,学有所得。(群:自动化测试-夜行者:816489363)

    --成都-阿木木

    框架说明

    官方文档:https://docs.pytest.org/en/latest/contents.html

    感谢慕城南风的博客:https://blog.csdn.net/lovedingd/article/details/98952868

    Pytest支持的插件库:https://plugincompat.herokuapp.com/

    第三方插件库:https://docs.pytest.org/en/latest/plugins.html

    pytest兼容unittest

    pytest兼容以前的unittest,只需要少量的更改代码即可,下面就大家熟悉的setup、teardown以及html报告进行说明

    1. setup和teardown

    setup和teardown主要分为:类级、函数级。

    运行于测试方法前后:

    1. #!/user/bin/env python  
    2. # -*- coding: utf-8 -*-  
    3.   
    4. """  
    5. ------------------------------------  
    6. @Project : pyqt5_study  
    7. @Time    : 2020/8/4 9:33  
    8. @Auth    : chineseluo  
    9. @Email   : 848257135@qq.com  
    10. @File    : test_setup_teardown.py  
    11. @IDE     : PyCharm  
    12. ------------------------------------  
    13. """  
    14. import pytest  
    15.   
    16.   
    17. class TestSetupTeardown():  
    18.     def setup(self):  
    19.         print("运行于测试方法之前")  
    20.   
    21.     def teardown(self):  
    22.         print("运行于方法之后")  
    23.   
    24.     def test_01(self):  
    25.         print("这是第一个方法")  
    26.   
    27.     def test_02(self):  
    28.         print("这是第二个方法")  
    29.   
    30.   
    31. if __name__ == '__main__':  
    32.     pytest.main("-s test_setup_teardown.py")  

    运行结果:

    1. test_setup_teardown.py::TestSetupTeardown::test_01   
    2. 运行于测试方法之前  
    3. PASSED                [ 50%]这是第一个方法  
    4. 运行于方法之后  
    5.   
    6. test_setup_teardown.py::TestSetupTeardown::test_02   
    7. 运行于测试方法之前  
    8. PASSED                [100%]这是第二个方法  
    9. 运行于方法之后  

    运行于测试类的始末:

    1. #!/user/bin/env python  
    2. # -*- coding: utf-8 -*-  
    3.   
    4. """  
    5. ------------------------------------  
    6. @Project : pyqt5_study  
    7. @Time    : 2020/8/4 9:33  
    8. @Auth    : chineseluo  
    9. @Email   : 848257135@qq.com  
    10. @File    : test_setup_teardown.py  
    11. @IDE     : PyCharm  
    12. ------------------------------------  
    13. """  
    14. import pytest  
    15.   
    16.   
    17. class TestSetupTeardown():  
    18.     @classmethod  
    19.     def setup_class(self):  
    20.         print("运行于测试类之前")  
    21.   
    22.     @classmethod  
    23.     def teardown_class(self):  
    24.         print("运行于测试类之后")  
    25.   
    26.     def test_01(self):  
    27.         print("这是第一个方法")  
    28.   
    29.     def test_02(self):  
    30.         print("这是第二个方法")  
    31.   
    32.   
    33. if __name__ == '__main__':  
    34.     pytest.main(["-s","test_setup_teardown.py"])  
    1. html测试报告

    使用pytest的测试报告插件可以替换unittest本身的HTMLTestRunner报告

    安装:pip install pytest-html

    使用方式:命令行格式:pytest --html=用户路径/report.html

    pytest框架使用约束

    所有的单测文件名都需要满足test_*.py格式或*_test.py格式。

    在单测文件中,测试类以Test开头,并且不能带有 init 方法(注意:定义class时,需要以T开头,不然pytest是不会去运行该class的)

    在单测类中,可以包含一个或多个test_开头的函数。

    此时,在执行pytest命令时,会自动从当前目录及子目录中寻找符合上述约束的测试函数来执行。可以在pytest.ini中修改测试目录、测试模块、测试类、测试方法扫描进行默认修改。

    Pytest Exit Code含义清单

    程序运行成功结束控制台输出:Process finished with exit code 0  

    Exit code 0 所有用例执行完毕,全部通过

    Exit code 1 所有用例执行完毕,存在Failed的测试用例

    Exit code 2 用户中断了测试的执行

    Exit code 3 测试执行过程发生了内部错误

    Exit code 4 pytest 命令行使用错误

    Exit code 5 未采集到可用测试用例文件

    pytest之fixture

    1. Fixture作用

    fixture修饰器来标记固定的工厂函数,在其他函数,模块,类或整个工程调用它时会被激活并优先执行,通常会被用于完成预置处理和重复操作。

    fixture是在测试函数前后运行,由pytest执行的外壳函数;代码可以定制,满足多变的测试需求,包括定义传入测试中的数据集,配置测试前系统的初始工作,为批量测试提供数据源等等,fixture是pytest用于将测试前后进行预备,清理工作的代码分离出核心测试逻辑的一种机制

    1. 说明

    @pytest.fixture()装饰器用于申明函数是一个fixture,如果测试函数的参数列表中包含fixture,那么pytest会检测到,检测顺序是,优先搜索该测试所在的模块,然后搜索conftest.py,并在测试函数运行之前执行该fixture,fixture可以完成测试任务,也可以返回测试数据给测试函数

    scope:被标记方法的作用域

    function" (default):作用于每个测试方法,每个test都运行一次

    "class":作用于整个类,每个class的所有test只运行一次

    "module":作用于整个模块,每个module的所有test只运行一次

    "session:作用于整个session(慎用),每个session只运行一次

    params:(list类型)提供参数数据,供调用标记方法的函数使用

    autouse:是否自动运行,默认为False不运行,设置为True自动运行

    pytest --setup-show test_example1.py(可以看到执行过程顺序)

    3、测试数据返回(参数化)

    A.返回测试数据

    1. # coding:utf-8  
    2. import pytest  
    3.   
    4.   
    5. # @pytest.fixture()不传参,默认是function级别的,也就是只在test开头函数前后执行  
    6. @pytest.fixture()  
    7. def fixture_test():  
    8.     print("方法执行前执行")  
    9.     yield  
    10.     print("方法执行后执行")  
    11.   
    12. def test_data(fixture_test):  
    13.     assert 2 == 2  
    1. 返回测试数据
    1. # coding:utf-8  
    2. import pytest  
    3.   
    4.   
    5. # @pytest.fixture()不传参,默认是function级别的,也就是只在test开头函数前后执行;也可以使用fixture返回数据  
    6. @pytest.fixture()  
    7. def fixture_test():  
    8.     return [1, 2, 3, 4]  
    9.   
    10. def test_data(fixture_test):  
    11.     print(fixture_test[1])  
    12.     assert 2 == fixture_test[1]  

    4、Fixture函数存放位置

    1. 单个测试模块文件内,只有该模块文件的类和方法可以访问到该fixture函数
    2. 如果希望多个测试文件共享fixtrue,可以在某个公共目录下新建一个fixture,将fixture放在里面

    1. Fixture作用范围
    1. function级别作用域

    function每个函数或方法都会调用(有两种写法,不传递参数,默认就是function,也可以指定scope="function",来进行作用域的指定

    1. @pytest.fixture()  
    2. def fixture_function():  
    3.     print("fixturetest测试1")  
    4.     return 1  
    5.   
    6. @pytest.fixture(scope="function"):  
    7. def fixture_function():  
    8.     print("fixture测试2")  
    9.     return 2  
    10.   
    11. def test_fixture(fixture_function1):  
    12.     assert 1 == fixture_function1  
    1. Class级别作用域
    1. # coding:utf-8  
    2.   
    3. import pytest  
    4.   
    5.   
    6. # @pytest.fixture(scope="class")只在类的前后执行一次  
    7. @pytest.fixture(scope="class")  
    8. def fixture_class():  
    9.     print("类前执行一次")  
    10.     yield  
    11.     print("类后执行一次")  
    12.   
    13.   
    14. class TestCase:  
    15.     def test_1(self, fixture_class):  
    16.         print("类方法")  
    1. Module级别作用域
    1. # coding:utf-8  
    2. import pytest  
    3.   
    4.   
    5. @pytest.fixture(scope="module")  
    6. def fixture_module():  
    7.     print("模块执行前执行")  
    8.     yield  
    9.     print("模块执行后执行")  
    10.   
    11.   
    12. def test_1(fixture_module):  
    13.     print(" 测试方法")  
    14.   
    15.   
    16. class TestCase:  
    17.     def test_2(self, fixture_module):  
    18.         print(" 类方法")  
    1. Session级别作用域

    session是多个文件调用一次,可以跨越.py文件调用,每个.py文件都是module

    当我们有多个.py文件的用例时,如果多个用例只需要调用一次fixture,可以设置scope="session",并且写入到conftest。py文件里面

    1. import pytest  
    2.   
    3.   
    4. @pytest.fixture(scope="session")  
    5. def fixture_session():  
    6.     print("全局前执行一次")  
    7.     yield  
    8.     print("全局后执行一次")  
    9.   
    10. # coding:utf-8  
    11. import pytest  
    12.   
    13.   
    14. def test_1(fixture_session):  
    15.     print("方法")  
    16.   
    17.   
    18. class TestCase:  
    19.     def test_2(self, fixture_session):  
    20.         print("类方法")  

    pytest之配置文件

    1. pytest非测试文件介绍

    1、pytest.ini:pytest的主配置文件,可以改变pytest的默认行为,其中有很多可以配置的选项,包含日志,命令行的一些参数,控制台输出的信息等等

    2、conftest.py:是本地的插件库,其中的hook函数和fixture将作用于该文件所在目录以及所有子目录

    1. 如何查看pytest.ini选项

    使用pytest --help查看pytest.ini所有设置选项

    1. 如何更改默认命令行选项

    pytest -v --verbose 可以输出详细信息

    [pytest]

    addops = -v --alluredir ./allure-result(addopts增加默认执行的操作步骤,简化命令行参数)(allure测试报告默认在json文件目录下生成,可以使用allure generate jsonpathdir -o allurepathdir更改)

    ps:

    如何使用allure生成测试报告

    1、brew install allure

    2、安装allure-pytest

    3、运行case时增加命令行选项pytest -v --allure ./allure-results test.py

    4、生成测试报告allure generate allure-results -o allure

    有哪些常用的命令行选项呢?

    -v:输出详细信息,显示具体执行了那些测试用例

    --collect-only 展示在给定的配置下那些测试用例会被执行,仅用于展示,不执行

    -k 允许使用表达式指定希望运行的测试用例

    exp:pytest -v -k 'baidu' test.py(在pytest中查找含有baidu关键字的case执行)

    -m marker用于标记测试并分组

    --strict 遇到mark拼写错误会检查,与mark配合使用

    1. 注册标记防范拼写错误

    自定义标记可以简化测试工作,但是标记容易拼写错误,默认情况下不会引起错误,pytest以为这是另外一个标记,为了避免拼写错误,可以在pytest.ini文件里进行注册

    markers = data_file:a test_data get_and_format marker

    通过命令查看:pytest --help(或者pytest --marks)没有注册的标记不会出现在markers列表里面,如果使用--strict选项,遇到拼写错误的标记或者未注册的标记会报错

    如果自己增加一个测试函数的标记呢?

    @pytest.mark.smoke

    pytest -m 'smoke' test.py

    1. 执行pytest的最低版本号设置

    minversion = 6.0

    minversion选项可以指定运行测试用例的pytest的最低版本

    1. 指定pytest忽略某些目录

    norecursedirs = .*data config utils

    可以使用norecursedirs缩小pytest的搜索范围

    指定访问目录

    testpath = testsdir

    1. 配置日志

    通过将log_cli配置选项设置为true,pytest将在直接将日志记录发送到控制台时输出日志记录。

    您可以指定传递的级别,以将等于或更高级别的日志记录打印到控制台的日志记录级别--log-cli-level。此设置接受python文档中显示的日志记录级别名称,或者接受整数作为日志记录级别num。

    此外,您还可以指定--log-cli-format和 --log-cli-date-format哪个镜和默认--log-format和 --log-date-format如果没有提供,但只被应用到控制台日志处理程序。

    还可以在配置INI文件中设置所有CLI日志选项。选项名称为:

    log_cli_level

    log_cli_format

    log_cli_date_format

    如果您需要将整个测试套件的日志记录记录到一个文件中,则可以传递 --log-file=/path/to/log/file。该日志文件以写模式打开,这意味着它将在每个运行测试会话中被覆盖。

    您还可以通过传递日志文件的日志记录级别 --log-file-level。此设置接受python文档中所见的日志记录级别名称(即大写的名称),或者接受整数作为日志记录级别num。

    此外,您还可以指定--log-file-format和 --log-file-date-format,它们等于--log-format和 --log-date-format但应用于日志文件日志处理程序。

    还可以在配置INI文件中设置所有日志文件选项。选项名称为:

    log_file

    log_file_level

    log_file_format

    log_file_date_format

    您可以调用set_log_path()以动态自定义log_file路径。此功能被认为是实验性的。

    1. [pytest]  
    2. testpaths = TestCases  
    3. log_format = %(asctime)s %(levelname)s %(message)s  
    4. log_level = INFO  
    5. log_file_level = debug  
    6. log_file_date_format = %Y-%m-%d %H:%M:%S  
    7. log_file_format = %(asctime)s %(levelname)s %(message)s  
    8. ;log_file = ../../Logs/log.log  
    9. log_cli = True  
    10. log_cli_level = INFO  
    11. log_cli_format = %(asctime)s [%(levelname)1s] %(message)s (%(filename)s:%(lineno)s)  
    12. log_cli_date_format=%Y-%m-%d %H:%M:%S  

    8、配置例子

    #配置pytest命令行运行参数

    [pytest]

    addopts = -s ... # 空格分隔,可添加多个命令行参数 -所有参数均为插件包的参数配置测试搜索的路径

    testpaths = ./scripts # 当前目录下的scripts文件夹 -可自定义

    #配置测试搜索的文件名称

    python_files = test*.py

    #当前目录下的scripts文件夹下,以test开头,以.py结尾的所有文件 -可自定义

    配置测试搜索的测试类名

    python_classes = Test_*

    #当前目录下的scripts文件夹下,以test开头,以.py结尾的所有文件中,以Test开头的类 -可自定义

    配置测试搜索的测试函数名

    python_functions = test_*

    #当前目录下的scripts文件夹下,以test开头,以.py结尾的所有文件中,以Test开头的类内,以test_开头的方法 -可自定义

    pytest之数据驱动

    1. Fixture
    1. import pytest  
    2. @pytest.fixture(params=[1, 2, 3])  
    3. def need_data(request): # 传入参数request 系统封装参数  
    4.     return request.param # 取列表中单个值,默认的取值方式  
    5. class Test_ABC:  
    6.    
    7.     def test_a(self,need_data):  
    8.         print("------->test_a")  
    9.         assert need_data != 3 # 断言need_data不等于3  
    10.    
    11. if __name__ == '__main__':  
    12.     pytest.main("-s  test_abc.py")  
    1. parametrize装饰器

    @pytest.mark.parametrize(argnames,argvalues)装饰器可以达到批量传送参数的目的,argvalues里面传递的是元组或者列表里面嵌套元组的方式

    pytest插件与hook函数

    1. 简介

    pytest可以通过添加插件可以扩展功能,pytest的代码结构适合定制和扩展插件,可以借助hook函数来实现。把fixture函数或者hook函数添加到conftest文件里,这种方式,就已经创建了一个本地的conftest插件!!!

    1. pytest plugin加载的几种方式

    1、内置plugins:从代码内部的_pytest目录加载;

    2、外部插件(第三方插件):通过setuptools entry points机制发现的第三方插件模块;

    推荐使用的第三方的pytest插件:https://docs.pytest.org/en/latest/plugins.html

    3、conftest.py形式的本地插件:测试目录下的自动模块发现机制

    通过pytest --trace-config命令可以查看当前pytest中所有的plugin

    在pytest中,所谓的plugin其实就是能被pytest发现的一些带有pytest hook方法的文件或者对象

    1. 什么是hook方法(钩子函数)

    使用的框架提供公用的规则,其他开发者使用这个规则编写的文件或者代码可以被框架识别,框架进行初始化时,会收集满足这个规则的所有代码(文件),然后将这些代码加入到框架中来,在执行时,一并进行初始化。所有这一规则下可以被框架收集到的方法就是hook方法。

    1. 编写自己的插件

    插件可以改变pytest行为,可用的hook函数很多,详细的定义:

    http://doc.pytest.org/en/latest/_modules/_pytest/hookspec.html

    1、pytest_addoption,基本每个pytest plugin都会有这个hook方法,它的作用是为pytest命令添加自定义的参数

    parser:用户命令行参数与ini文件值的解析器

    def pytest_addoption(parser):

    parser.addoption("--env",##注册一个命令行选项

    default="test",#默认值为test

    dest="env",

    help="set test run env")#说明

    pytest_addoption:Hook function,这里创建了一个argparser的group,通过addoption方法添加option,使得显示help信息时相关option显示在一个group下面,更加友好,使用pytest --help可以查看

    1. def pytest_addoption(parser):  
    2.     group = parser.getgroup("chinese auto test")  
    3.     group.addoption("--env", default="ggg", dest="env", help="test env")  
    4.     group.addoption("--env2", default="ggg", dest="env", help="test env")  
    5.   
    6. @pytest.fixture(scope="session")  
    7. def cmdopt(request):  
    8.     print("获取不同环境变量的配置")  
    9.     return request.config.getoption("--env")  

    2、pytest_collection_modifyitems,是一个完成所有测试项的收集后,pytest调用的钩子

    def pytest_collection_modifyitems(items):

    pass

    测试用例收集完成后,将收集到的item的name和nodeid的中文显示在控制台上,所有的测试用例收集完毕后调用,可以再次过滤或者对它们重新排序

    items(收集的测试项目列表)

    1. def pytest_collection_modifyitems(items):  
    2.     print("test hook 函数")  
    3.     for item in items:  
    4.         item.name = item.name.encode("utf-8").decode("unicode_escape")  
    5.         item._nodeid = item._nodeid.encode("utf-8").decode("unicode_escape")  

    Pytest高级用法

    1、跳过测试函数

    根据特定的条件,不执行标识的测试函数.

    方法:

    skipif(condition, reason=None)

    参数:

    condition:跳过的条件,必传参数

    reason:标注原因,必传参数

    使用方法:

    @pytest.mark.skipif(condition, reason="xxx")

    1. import pytest  
    2. class Test_ABC:  
    3.     def setup_class(self):  
    4.         print("------->setup_class")  
    5.     def teardown_class(self):  
    6.         print("------->teardown_class")  
    7.     def test_a(self):  
    8.         print("------->test_a")  
    9.         assert 1  
    10.     @pytest.mark.skipif(condition=2>1,reason = "跳过该函数") # 跳过测试函数test_b  
    11.     def test_b(self):  
    12.         print("------->test_b")  
    13.             assert 0  

    2、标记为预期失败的函数

    标记测试函数为失败函数

    方法:

    xfail(condition=None, reason=None, raises=None, run=True, strict=False)

    常用参数:

    condition:预期失败的条件,必传参数

    reason:失败的原因,必传参数

    使用方法:

    @pytest.mark.xfail(condition, reason="xx")

    1. import pytest  
    2. class Test_ABC:  
    3.     def setup_class(self):  
    4.         print("------->setup_class")  
    5.     def teardown_class(self):  
    6.         print("------->teardown_class")  
    7.     def test_a(self):  
    8.         print("------->test_a")  
    9.         assert 1  
    10.     @pytest.mark.xfail(2 > 1, reason="标注为预期失败") # 标记为预期失败函数test_b  
    11.        def test_b(self):  
    12.            print("------->test_b")  
    13.           assert 0  
    1. 函数参数化

    方便测试函数对测试属于的获取。

    方法:

    parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)

    常用参数:

    argnames:参数名

    argvalues:参数对应值,类型必须为list

    当参数为一个时格式:[value]

    当参数个数大于一个时,格式为:[(param_value1,param_value2.....),(param_value1,param_value2.....)]

    使用方法:

    @pytest.mark.parametrize(argnames,argvalues)

    ️ 参数值为N个,测试方法就会运行N次

    在函数参数化中还可以传递函数,进行参数化

    1. import pytest  
    2. def return_test_data():  
    3.     return [(1,2),(0,3)]  
    4. class Test_ABC:  
    5.     def setup_class(self):  
    6.         print("------->setup_class")  
    7.     def teardown_class(self):  
    8.             print("------->teardown_class")  
    9. @pytest.mark.parametrize("a,b",return_test_data()) # 使用函数返回值的形式传入参数值  
    10. def test_a(self,a,b):  
    11.     print("test data:a=%d,b=%d"%(a,b))  
    12.     assert a+b == 3  

    4、修改python traceback输出

    pytest --showlocals # show local variables in tracebacks

    pytest -l # show local variables (shortcut)

    pytest --tb=auto # (default) 'long' tracebacks for the first and last

    # entry, but 'short' style for the other entries

    pytest --tb=long # exhaustive, informative traceback formatting

    pytest --tb=short # shorter traceback format

    pytest --tb=line # only one line per failure

    pytest --tb=native # Python standard library formatting

    pytest --tb=no # no traceback at all

    python --full-trace 参数会打印更多的错误输出信息,比参数 --tb=long 还多,即使是 Ctrl+C 触发的错误,也会打印出来

    5、获取用例执行的性能数据

    获取最慢的10个用例的执行耗时

    pytest --durations=10

    Pytest-xdist进程级并发插件讲解

    参考夜行者自动化测试群(群号:816489363)文件:pytest-xdist进程级并发参数化说明--成都-阿木木

    Pytest常用插件介绍

    1. pytest-assume

    多重校验插件,可以执行完所有的断言,常规assert断言执行失败后下面的断言便会停止,不在执行,assume插件可以执行完所有断言

    安装命令:pip install pytest-assume

    使用方式:

    1. def test_add_case(self):  
    2.     pytest.assume(add(1,2)==3)  
    3.     pytest.assume(add(1,4)==3)  
    4.     pytest.assume(add(2,2)==4)  
    1. pytest-ording

    Pytest用例默认执行顺序是和collect的顺序一致,用例收集是按照测试目录开始,由上到下,在测试模块中,测试用例收集也是从上到下,需要调整测试函数的执行顺序可以通过pytest_collection_modifyitems这个hook函数(钩子)进行插件编写。在pytest的第三方插件中,已经有人实现了这个功能,下面介绍pytest-ordering这个插件。

    安装命令:pip install pytest-ordering

    1. @pytest.mark.run(order=2)  
    2. def test_order1():  
    3.     print ("first test")  
    4.     assert True  
    5. @pytest.mark.run(order=1)  
    6. def test_order2():  
    7.     print ("second test")  
    8.     assert True  
    1. pytest-rerunfailures

    失败重跑插件,使用比较简单,在脚本运行过程中,可能某些原因导致用例执行失败,可能是网络加载等,可以使用该插件,对于失败的用例进行重跑,提高报告的准确性。

    安装命令:pip install pytest-rerunfailures

    命令行指定:

    Pytest -s test_xxx.py --reruns 5 #表示失败用例运行五次

    Pytest -s test_xxx.py --reruns-delay 2 #表示失败用例等待2S后在执行

    在装饰器中指定:

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

    显示进度条,控制台显示比较好看

    显示效果如下:

    安装命令:pip install pytest-sugar

    (群:自动化测试-夜行者:816489363)

  • 相关阅读:
    nil / Nil / NULL / NSNull
    When should I use nil and NULL in Objective-C?
    stream_context_create解析
    Linux如何通过命令查看日志文件的某几行(中间几行或最后几行)
    MySQL索引失效的几种场景
    Python网络爬虫精要
    深入Redis持久化
    SVN和Git 介绍,区别,优缺点以及适用范围
    php提示Notice: Undefined index解决方法
    在线更换背景网站(白色背景换为蓝色背景证件照)
  • 原文地址:https://www.cnblogs.com/chineseluo/p/13703921.html
Copyright © 2020-2023  润新知