当前位置 博文首页 > 个人微信:xiaobotester,添加进行学习交流请备注。 个人微信公

    个人微信:xiaobotester,添加进行学习交流请备注。 个人微信公

    作者:[db:作者] 时间:2021-07-03 13:03

    pytest是一个测试框架,功能与unittest类似,完全兼容unittest的功能。一般做接口测试的时候,以前用的多的是python+requests+httptestrunner完成接口自动化测试与报告生成,看到现在很多都在用pytest框架,我也来学习一下,接口测试中pytest应用广泛的是通过python+pytest+allure生成测试报告,报告格式比较美观。

    文章比较长,先简单概述一下本文的大概内容:

    1、环境搭建以及pytest是怎么运行的,如何识别有效用例

    2、用例执行顺序、参数传递、数据驱动

    3、断言,以及常见的pytest装饰器

    4、测试报告生成,包括自带的pytest的报告以及集成allure的报告。

    ?

    pytest安装

    pip install -U pytest

    查看安装版本:

    cmd窗口输入:pytest --version ,会在窗口中输出类似下面格式的一句话:

    This is pytest version 5.4.3, imported from d:\python38\lib\site-packages\pytest\__init__.py

    ?

    用例的识别与运行

    用例编写规范

    • 测试文件以test_开头(或者以_test结尾)

      pytest会找当前以及递归查找子文件夹下面所有的test_*.py或*_test.py的文件,把其当作测试文件(除非显式指定文件所在路径)

    • 测试类名称以Test开头,并且不能带有init方法

      如果类名称以Test开头的class类中包含了init方法,则会触发告警,提示PytestCollectionWarning: cannot collect test class 'TestXXX'

    • 测试函数以test_开头

    • 断言使用基本的assert即可

    运行参数

    你们可能会有这样的疑问,现在大家都在用类似pycharm的IDE工具,为什么还要去学习命令行运行的参数和方式呢?

    pytest框架是一个测试框架,如果需要集成到jenkins上的话,是需要用命令行的方式去执行的,有时候要执行多个用例的时候,用命令行文件比较方便。

    pytest可以在命令行执行,在命令行执行的时候,可以带很多参数,下面介绍几种常用到的参数用法:(使用pytest --help可以看到命令参数的帮助文档)

    • 不带参数执行

    使用方法:pytest 或者 py.test ?, ?将会读取当前路径下所有符合规则的文件,类,方法,函数全部执行

    • -v 参数

    打印详细运行的日志信息,方便定位问题

    • -s参数

    可以在控制台输出结果,当代码中有用到print语句输出信息时,不加这个参数的话,控制台是不会显示print的内容的

    • -k参数

    使用该参数可以指定运行满足要求的用例。用法如下:

    pytest?-k?"类名"pytest -k "方法名"pytest?-k?"类名?and?not?方法名"

    注意: -k参数后面跟的引号只能用双引号"",不能用单引号'',否则不会识别到用例,运行会报错

    • -x参数

    遇到用例执行失败或断言失败,立即停止运行,不执行后面的用例。

    • --maxfail参数

    设置允许失败的用例数,超过这个阈值时,停止运行。

    pytest --maxfail=num?,失败用例数>=num时,停止运行?

    • -m参数

    按照标签名运行所有包含某个标签的用例,需要在测试用例上面都加上装饰符@pytest.mark.标记名。使用-m选项,可以使表达式指定多个标记名。使用-m "mark1 and mark2"可以同时选中带有这两个标记的所有测试用例。使用-m "mark1 and ?not mark2"则会选中带mark1标记且不带mark2标记的测试用例,使用-m "mark1 or mark2"则会选中带有mark1或者mark2的所有测试用例。

    用例标记使用用法如下:

    import pytest@pytest.mark.mark1@pytest.mark.mark2def?test_a002(self):????print('this?is?test_a002?method')

    使用-m参数运行时,有可能会提示

    PytestUnknownMarkWarning: Unknown pytest.mark.xxx - is this a typo?

    这只是一个告警,不影响实际执行结果。处理方式有以下几种:

    方法一:在测试用例文件的根目录新建conftest.py,内容如下:???????

    # 单标签处理方式def pytest_configure(config):    config.addinivalue_line(       "markers", "mark1"   # test 是标签名    )
    # 多标签处理方式def pytest_configure(config):    marker_list = ["mark1", "mark2"]  # 标签名集合    for markers in marker_list:        config.addinivalue_line(        "markers", markers    )

    方法二:在项目根路径或者用例目录下新建一个pytest.ini文件,内容如下:???????

    [pytest]markers=    mark1    mark2    mark3或者用如下格式:[pytest]markers=????mark1:this?is?test1?method mark????mark2:this?is?test2?method?mark????mark3:this?is?test3?method mark?

    注意:有多个mark的时候,换行写,要注意缩进,不缩进是无法识别的,每个mark名称后面是可以加冒号,然后备注mark的相关详细描述信息。

    方法三:在pytest.ini文件中设置告警过滤,这样可以避免由于mark标记使用过多时,要一个一个配置,比较麻烦。

    具体使用方法可以参考官方文档:

    https://docs.pytest.org/en/latest/warnings.html???????

    [pytest]addopts = -p no:warnings或者:[pytest]filterwarnings =    error    ignore::UserWarning

    运行模式

    pytest提供了多种运行模式,可以指定某个模块,执行单个pytest模块进行调试,也可以单独运行某个类下面的某个测试方法。

    命令行运行具体使用方法如下:???????

    pytest?文件名.pypytest 文件名.py::类名pytest 文件名.py::类名::方法名

    也可以在pycharm中运行pytest用例

    1、先打开Pycharm设置->Tools->Python Integrated Tools->Testing:pytest????

    (需要安装pytest依赖,然后符合编写规则的测试用例都能被pycharm识别出来,会在用例前面出现一个绿色的执行按钮,点击这个按钮就能执行某个方法或者某个类)

    ?

    pytest 框架结构

    与unittest类似,执行用例前后会执行setup、teardown来增加用例的前置和后置条件。

    pytest的前置和后置条件大概有这么几类:

    • 模块级(setup_module/teardown_module)

    在模块始末调用

    • 函数级(setup_function/teardown_function)

    在函数始末调用(在类外部)

    • 类级(setup_class/teardown_class)

    在类始末调用(在类中)

    • 方法级(setup_method/teardown_method)

    在方法始末调用(在类中)

    • 方法级(setup/teardown)

    在方法始末调用(在类中)

    调用顺序:

    setup_module>setup_function>teardown_function>setup_class>setup_method>setup>teardown>teardown_method>teardown_class>teardown_module

    注意事项:

    1、其中函数级的setup_function/teardown_function是在class类外部调用的,写在class类中是没用的,不会调用

    2、(setup_method/teardown) 与 (setup/teardown)功能是一样的,优先级是先执行setup_method,在执行setup。一般二者用其中一个即可.

    验证上面的执行顺序,可以执行下面的脚本,???????

    在一个test开头的py文件里面,编写一下脚本:
    def setup_module():
        print('\n 这是setup_module方法,只执行一次,当有多个测试类的时候使用')
    def teardown_module():
        print('这是 teardown_module方法,只执行一次,当有多个测试类的时候使用')
    def teardown_module():
        print('这是 teardown_module方法,只执行一次,当有多个测试类的时候使用')
    def setup_function():
        print('这是 setup_function方法,只执行一次,当有多个测试类的时候使用')
    def teardown_function():
        print('这是 teardown_function方法,只执行一次,当有多个测试类的时候使用')
    def test_five():
        print('this is test_five method')
    def test_six():
        print('this is test_six method')
    class TestPytest01:
    
        def setup_class(self):
            print('调用了setup_class1方法')
    
        def teardown_class(self):
            print('调用了teardown_class1方法')
    
        def setup_method(self):
            print('执行测试方法前的setup1操作')
    
        def teardown_method(self):
            print('执行测试方法后的teardown1操作')
    
        def test_one(self):
            print('this is test_one method')
    
        def test_two(self):
            print('this is test_two method')
    
        def setup(self):
            print('this is setup 方法')
        def teardown(self):
            print('this is teardown 方法')
    
    class TestPytest02:
        def setup_class(self):
            print('调用了setup_class2方法')
    
        def teardown_class(self):
            print('调用了teardown_class2方法')
    
        def setup_method(self):
            print('执行测试方法前的setup2操作')
    
        def teardown_method(self):
            print('执行测试方法后的teardown2操作')
    
        def test_three(self):
            print('this is test_three method')
    
        def test_four(self):
            print('this is test_four method')

    然后看打印的输出结果:

    控制用例的执行顺序

    pytest默认的执行顺序是按照文件名以及测试方法名称排序执行的,如果想指定用例的顺序,可以使用pytest-ordering插件,在测试方法前面加上装饰器@pytest.mark.run(order=num),就可以按照num的大小顺序来执行。

    安装:

    pip install pytest-ordering

    案例:

    import pytest
    class TestPytest:
        @pytest.mark.run(order=-2)
        def test_03(self):
            print('test_03')
    
        @pytest.mark.run(order=-3)
        def test_01(self):
            print('test_01')
    
        @pytest.mark.run(order=4)
        def test_02(self):
            print('test_02')

    执行结果如下:

    注意:按照num排序时,正整数在前,负数在后面,然后统一按照从小到大的顺序执行。(我目前使用的是pytest5.4.3版本,不排除以后版本更改排序规则)

    ?

    pytest fixtures

    pytest中可以使用@pytest.fixture装饰器来装饰一个方法,被装饰方法的方法名可以作为一个参数传入到测试方法中。可以通过这种方式来完成测试之前的初始化操作,也可以返回数据给测试函数。???????

    import?pytestclass?TestFixture:    @pytest.fixture()    def login(self):        return 11
        def test_001(self, login):        assert 1+10 ==login

    ?

    fixture的scope参数:

    根据作用范围大小范围划分,分别是:session>module>class>function.

    @pytest.fixture(scope='function') scope的默认值是function
    • function函数或者方法级别都会被调用

    • class类级别调用一次

    • module模块级别调用一次

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

    通过以下脚本可以测试一下scope的作用范围:

    通过更改scope的枚举值,即可看到效果,可以看到print('调用login方法')在不同的scope选项下,打印出来的次数是不一样的。???????

    import pytest
    @pytest.fixture(scope='class')def login():    print('调用login方法')    return 11
    class TestFixture:
        def test_001(self, login):        print('this is test_001方法')        assert 1+10 ==login
        def test_002(self, login):        print('this is test_001方法')        assert 1+10 ==login
    class TestFixture1:
        def test_003(self, login):        print('this is test_003方法')        assert 1+10 ==login

    conftest.py文件

    一般用于scope='session'级别的fixture。conftest.py被pytest视为一个本地插件库,使用conftest.py的规则:

    1、conftest.py这个文件名是固定的,不可以更改

    2、conftest.py与运行用例在同一个包下,并且该包中要有__init__.py文件

    3、使用的时候不需要导入conftest.py,pytest会自动加载,放到哪个package下,就在这个package内有效。

    ?

    fixture的autouse参数:

    如果想让每条测试用例都添加fixture功能,那么可以使用@pytest.fixture里面的autouse参数,autouse='true'则会自动应用到所有用例中。

    用法如下:

    使用fixture传递测试数据

    fixture的param参数可以用来传递测试数据,实现数据驱动的效果,避免出现冗余代码。可以大大减少代码量,并且便于阅读和维护。传入的数据需要使用一个固定的参数名request来接收,代码如下:???????

    
    import pytest
    
    @pytest.fixture(params={1,2,3})
    def data(request):
        return request.param
    
    def test_data(data):
        print("测试数据{data}")
        assert data<10

    运行结果:

    ?

    pytest使用pytest-xdist并行运行测试

    pytest-xdist是pytest里面的一个分布式执行的插件,可以多个CPU或主机执行。这个是进程级的并发,需要保证测试用例之间的独立性,插件是动态决定测试用例执行顺序,如果互相之间有依赖,可能会导致执行失败/达不到预期的结果。

    安装:pip install pytest-xdist

    用法:

    pytest -n auto 或者 pytest -n num?

    参数为auto时,系统会自动检测CPU核数,如果参数为num数字的话,则表示指定运行的处理器进程数量。

    ?

    pytest使用pytest-html生成简易测试报告

    安装:pip install pytest-html

    使用方法:pytest --html=xxxx/report.html ??(通过这种方式,生成的html报告,css文件是独立的,发给其他人的时候要一起带上css样式文件)

    pytest --html=xxxx/report.html?--self-contained-html??(使用self-contained-html?参数,会将css样式文件的内容直接写到html文件中)

    生成的报告样式如下:

    ?

    报告中会包含Environment和Summary以及Results的相关数据,如果想要在Environment和Summary下添加一些个性化的内容展示到报告中的话,可以在conftest.py文件中添加以下代码:???????

    import pytestfrom py._xmlgen import html
    # Environment下添加配置项或者修改已有配置项d的值def pytest_configure(config):    config._metadata['测试地址'] = '192.168.1.XXX'    config._metadata['项目描述'] = '这是XXX项目测试报告'    config._metadata['Python'] = '2.7'  #报告中默认会有python版本,可以自己手动修改
    #?Summary下添加个性化的内容展示@pytest.mark.optionalhookdef pytest_html_results_summary(prefix, summary, postfix):    prefix.extend([html.p("测试人: 小博")])
    

    加上以上代码后,运行生成的报告如下:

    ?

    pytest断言

    使用过unittest框架的都知道,unittest里面封装了很多的断言方法,有assertEqua、assertNotEqual等好几十个断言的方法,在pytest中,断言直接使用assert关键字就行:

    assert xx:判断xx为真

    assert not xx:判断xx不为真

    assert a in b:判断b是否包含a

    assert a == b:判断a等于b

    assert a !=b:判断a不等于b

    断言要做什么判断,可以自己去定义。也可以在assert后面加上断言失败后的描述信息:

    assert??a>b,'断言失败,实际结果是a<b'


    pytest parametrize参数化

    先来看一下parametrize()的方法源码:

    def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None):
    • 主要参数说明:

    argsnames:参数名,是一个字符串,多个参数名中间可以用逗号分隔

    argsvalues:参数对应的值,是由参数组成的列表,列表中有几个元素,就会生成几条用例,参数名和参数值的数量要相等。

    indirect:该参数值默认为False,表示argnames就是普通的参数,如果将该值设置为True,则可以用来将参数传入到fixture方法中。

    ids:用于标志用例的一个id字段,默认可以不传,会自动用argvalues填充,ids参数可以用来区分测试方法的标识。
    

    scope:声明argnames中参数的作用域,进而影响到测试用例的收集顺序

    • parametrize使用方法:

    单个参数:???????

    @pytest.mark.parametrize('a',[1,2,3,4] )def test_ddt01(a):    assert a<5

    输出结果:

    ?

    多个参数:???????

    @pytest.mark.parametrize('a,b',[("1+1",2),("1+2",3)])def test_ddt02(a,b):    assert eval(a) == b

    ?

    多次使用parametrize的用法:

    对同一个方法使用多次@pytest.mark.parametrize装饰器

    ???????

    @pytest.mark.parametrize('a',[1,2])@pytest.mark.parametrize('b',[1,2])def test_ddt03(a,b):    print(f'数据组合 a:{a}, b:{b}')

    ids参数用法及效果:

    ???????

    @pytest.mark.parametrize('a',[1,2,3 ],ids=('id-1','id-2','id-3' ))def test_ddt04(a):    assert a<5???????

    indirect用法:

    使用indirectTrue,pytest可以实现将参数传入到fixture方法中,也可以在当前测试用例中使用。

    ???????

    @pytest.fixture(scope='module')def fun_a(request):    print(f'fun_a:{request.param}')    return  request.param
    # indirect=True 声明fun_a是个函数@pytest.mark.parametrize('fun_a',[9,8,7] ,indirect=True)def test_ddt05(fun_a):    print(f'a:{fun_a}')    assert fun_a<10

    ?

    scope参数用法及结果演示:

    import pytest
    
    @pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4),(5,6)], scope='module')
    def test_scope1(test_input, expected):
        pass
    
    @pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4),(5,6)], scope='module')
    def test_scope2(test_input, expected):
        pass

    ?

    pytest结合YAML实现数据驱动

    在实际测试工作中,通常需要对多组不同饿的输入数据,进行同样的测试操作步骤,可以将多组测试数据以数据驱动的形式注入,可以做到测试数据和测试用例分别进行管理。常见的外部数据源可以用YAML、Json、Excel、CSV等方式进行管理。

    下面以YAML为例,简单演示一下如何实现数据驱动:

    安装: pip install PyYAML

    案例:

    创建一个testdata的文件夹,在下面创建data.yml和test_yaml.py文件,内容如下:???????data.yaml:

    -  - 1  - 2-  - 20  - 30
    
    test_yaml.py:import pytestimport yaml
    @pytest.mark.parametrize('a,b',yaml.safe_load(open('data.yml',encoding='utf-8')))def test_add(a,b):    print(f'a+b = {a+b}')

    输出结果:

    ?

    pytest结合allure生成测试报告

    Allure框架是一种灵活的、轻量级、支持多语言的测试报告工具,报告美观清晰、一目了然。同时支持多种语言,包括Java、Python、JavaScript、Ruby、Groovy、PHP、.Net、Scala等。

    环境搭建:

    1、以windows系统为例(先安装好JDK并配置环境变量),先下载allure的命令行工具进行安装。下载地址可从github上进行下载:

    https://github.com/allure-framework/allure2/releases

    github如果下载过慢的话,可以从我的网盘里面下载:

    链接:https://pan.baidu.com/s/1CJvL_jJuJhFAFP0olUnlCA ?

    提取码:1234

    下载最新的安装包后,解压,配置环境变量。

    新建一个ALLURE_HOME的环境变量,value指向解压后的根路径,,我电脑上的是:G:\devops\allure-2.9.0

    然后在PATH中加入%ALLURE_HOME%\bin?

    之所以要单独配置解压后的路径为ALLURE_HOME,是为了以后更换版本后更改环境变量比较方便。

    配置好后,在cmd窗口输入 allure --version 会打印出安装的版本。

    2、安装python的allure-pytest插件

    pip install -U allure-pytest

    ?

    具体使用方法:

    步骤一:在会用pytest执行用例的时候,指定参数 --allure选项及结果数据保存的目录:

    pytest --alluredir=./tempdir/data?

    pytest?--alluredir=./tempdir/data??--clean-alluredir?

    加--clean-alluredir选项会先清理数据目录,再重新生成新的数据,不清理也不会影响报告的生成。

    步骤二:

    • 使用allure serve 打开报告:

    在cmd窗口输入allure serve?./tempdir/data ,就会自动打开浏览器显示报告:

    ?

    • 使用allure ganerate命令生成html格式报告

    cmd窗口输入如下命令:

    allure generate?./tempdir/data -o ./report --clean?

    命令说明:

    ?./tempdir/data 指测试数据目录,?./report 指html报告生成的位置, --clean指先清空测试报告目录再重新生成新的测试报告。

    需要使用下面的命令打开报告,直接打开html文件,看不到数据:

    allure open -h 127.0.01 -p 8088? ./report/

    到此,allure报告就生成了,至于报告怎么去分析和查看,可以将报告切换为中文版本自己去进行分析即可。

    以上就是pytest常见的一些用法,适合新手入门了解,后续有时间会继续补充pytest的一些其他语法和用法以及扩展功能,欢迎关注小编,能及时获取下次更新喔!

    ?

    如果觉得这篇文章对你有帮助,请分享给身边的朋友一起学习,谢谢!

    ?

    cs