zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    HDU 2116 Has the sum exceeded
    HDU 1233 还是畅通工程
    HDU 1234 开门人和关门人
    HDU 1283 最简单的计算机
    HDU 2552 三足鼎立
    HDU 1202 The calculation of GPA
    HDU 1248 寒冰王座
    HDU 1863 畅通工程
    HDU 1879 继续畅通工程
    颜色对话框CColorDialog,字体对话框CFontDialog使用实例
  • 原文地址:https://www.cnblogs.com/chineseluo/p/13703921.html
Copyright © 2011-2022 走看看