一篇文章为你揭秘pytest的基本用法

2021-03-06 23:28:10 浏览数 (1)

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

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

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

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

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

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

pytest安装

代码语言:javascript复制
pip install -U pytest

查看安装版本:

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

This is pytest version 5.4.3, imported from d:python38libsite-packagespytest__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参数

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

代码语言:javascript复制
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的所有测试用例。

用例标记使用用法如下:

代码语言:javascript复制
import pytest
@pytest.mark.mark1
@pytest.mark.mark2
def test_a002(self):
    print('this is test_a002 method')

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

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

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

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

代码语言:javascript复制
# 单标签处理方式
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文件,内容如下:

代码语言:javascript复制
[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

代码语言:javascript复制
[pytest]
addopts = -p no:warnings
或者:
[pytest]
filterwarnings =
    error
    ignore::UserWarning

运行模式

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

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

代码语言:javascript复制
pytest 文件名.py
pytest 文件名.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。一般二者用其中一个即可.

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

代码语言:javascript复制
在一个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

案例:

代码语言:javascript复制
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装饰器来装饰一个方法,被装饰方法的方法名可以作为一个参数传入到测试方法中。可以通过这种方式来完成测试之前的初始化操作,也可以返回数据给测试函数。

代码语言:javascript复制
import pytest
class TestFixture:
    @pytest.fixture()
    def login(self):
        return 11

    def test_001(self, login):
        assert 1 10 ==login

fixture的scope参数:

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

代码语言:javascript复制
@pytest.fixture(scope='function') scope的默认值是function
  • function函数或者方法级别都会被调用
  • class类级别调用一次
  • module模块级别调用一次
  • session是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

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

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

代码语言:javascript复制
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 ==loginconftest.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来接收,代码如下:

代码语言:javascript复制
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文件中添加以下代码:

代码语言:javascript复制
import pytest
from 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.optionalhook
def 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使用方法:

单个参数:

代码语言:javascript复制
@pytest.mark.parametrize('a',[1,2,3,4] )
def test_ddt01(a):
    assert a<5

输出结果:

多个参数:

代码语言:javascript复制
@pytest.mark.parametrize('a,b',[("1 1",2),("1 2",3)])
def test_ddt02(a,b):
    assert eval(a) == b

多次使用parametrize的用法:

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

代码语言:javascript复制
@pytest.mark.parametrize('a',[1,2])
@pytest.mark.parametrize('b',[1,2])
def test_ddt03(a,b):
    print(f'数据组合 a:{a}, b:{b}')

ids参数用法及效果:

代码语言:javascript复制
@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方法中,也可以在当前测试用例中使用。

代码语言:javascript复制
@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参数用法及结果演示:

代码语言:javascript复制
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文件,内容如下:

代码语言:javascript复制
data.yaml:
-
  - 1
  - 2
-
  - 20
  - 30
代码语言:javascript复制
test_yaml.py:
import pytest
import 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

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

新建一个ALLURE_HOME的环境变量,value指向解压后的根路径,,我电脑上的是:G:devopsallure-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的一些其他语法和用法以及扩展功能,欢迎关注小编,能及时获取下次更新喔!

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

0 人点赞