pytest框架初认识

本文详细介绍了Pytest框架的使用,包括环境准备、常用参数、数据驱动、测试用例的前置/后置操作、mark的使用、配置文件pytest.ini的设置、Allure报告的生成以及插件的应用。重点讲解了数据驱动的实现方式、fixture的使用以及如何通过mark定制化运行测试用例。同时,还提到了如何通过pytest.ini配置文件优化测试行为和管理标记。
摘要由CSDN通过智能技术生成

一、Pytest环境准备及注意事项

1、安装pytest:pip install pytest
2、Pytest框架注意事项:
  2.1 测试文件.py以test开头或结尾
  2.2 测试用例类必须以Test开头,不能带有init方法
  2.3 测试用例函数以test开头或结尾
  2.4 断言必须使用assert

二、Pytest常用参数

pytest -v :输出详细信息
pytest -s : 输出打印信息
pytest -k xx xxx.py: 根据用例名称筛选测试用例
pytest --collect-only:展示在给定的配置下哪些测试用例会被执行,收集测试用例个数
pytest --setup-show:查看fixture的执行过程
pytest --html=报告路径:生成html报告,需要安装 pytest-html库:pip install pytest-html

assert语句,如果断言失败,它后面的语句不会执行

三、Pytest数据驱动(参数化)

Pytest是使用**@pytest.mark.parametrize**装饰器来实现数据驱动测试的
4.1 装饰测试类

import pytest

data = [(1,2,3),(2,1,1)]
@pytest.mark.parametrize(('a','b','expect'),data)
class Test_Calculate(object):

    def test_add(self,a,b,expect):
        assert a + b == expect

    def test_sub(self,a,b,expect):
        assert a - b == expect

当装饰(参数化)测试类时,传递的每一个数据列表或元组,类内所有的方法必须接收测试数据,否则会报错
4.2 装饰测试函数

  4.2.1 装饰单个数据

import pytest

data = [(1,2,3),(2,1,1)]
@pytest.mark.parametrize("a",data)
def test_print(a):
    print(a)

当测试用例只需要一个参数时,我们存放数据的列表如果嵌套子列表或元组,会把子列表或元组当成一个元素进行传递;
如果没有嵌套子列表,会将列表中的每一个元素进行传递;
解析:@pytest.mark.parametrize(‘a’, data)
  装饰器的第一个参数:需要一个变量接收列表中的每个元素,注意这个变量名必须与函数或方法定义时的形参名保持一致,测试用例需要使用同名的字符串接收测试数据
  装饰器的第二个参数:传递存储数据的列表,该对象可进行迭代,且列表有多少个元素就会生成并执行多少个测试用例
  4.2.2 装饰一组数据

import pytest

data = [(1,2,3),(2,1,1)]
@pytest.mark.parametrize("a,b,c",data)
def test_print(a,b,c):
    print(a,b,c)

当测试用例需要一组数据时,我们可以使用嵌套列表或嵌套元组来存放测试数据
解析:装饰器@pytest.mark.parametrize(“a,b,c”,data)
  装饰器的第一个参数:函数或方法定义时有几个形参,装饰器的第一个参数使用" "或’ ',将所有的形参变量当成一个整体进行包裹,或者将每一个形参变量名以字符串的形式包裹在一个元组中,来接收传递列表中子列表或元组的每一个元素; @pytest.mark.parametrize(“a,b,c”,data) ==》@pytest.mark.parametrize((“a”,“b”,“c”),data);当使用一组变量接收数据时,那么每个变量分别接收列表中的子列表或元组中的每个元素
  装饰器的第二个参数:传递存储数据的列表,且列表嵌套多少个子列表或元组,测生成多少条测试用例
  4.2.3 参数化-标记数据

# 标记参数化
@pytest.mark.parametrize("test_input,expected", [
    ("3+5", 8),
    ("2+4", 6),
    pytest.param("6 * 9", 42, marks=pytest.mark.xfail),
    pytest.param("6*6", 42, marks=pytest.mark.skip)
])
def test_mark(test_input, expected):
    assert eval(test_input) == expected

执行结果:

========== 2 passed, 1 skipped, 1 xfailed in 7.62s ========

四、测试用例前置/后置准备工作

pytest 提供的 fixture 实现 unittest 中 setup/teardown 功能,可以在每次执行case之前初始化数据。
不同点:fixture 可以只在执行某几个特定 case 前运行,只需要在运行 case 前调用即可。比setup/teardown 使用起来更灵活

def fixture(scope="function", params=None, autouse=False, ids=None,name=None):
"""
:arg scope:    可选四组参数:function(默认)、calss、module、package/session    
:arg params:   一个可选的参数列表,它将导致多个参数调用fixture函数和所有测试使用它。    
:arg autouse:  如果为True,则fixture func将为所有测试激活可以看到它。如果为False(默认值),则需要显式激活fixture。    
:arg ids:      每个参数对应的字符串id列表,因此它们是测试id的一部分。如果没有提供id,它们将从参数中自动生成。    
:arg name:     fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的同一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽; 解决这个问题的一种方法是将装饰函数命名 “fixture_ <fixturename>”然后使用”@ pytest.fixture(name='<fixturename>')”。
"""

Pytest环境初始化之conftest.py文件:
4.1 特点:
 4.1.1 conftest.py文件名字是固定的,不可以做任何修改
 4.1.2 文件和用例文件在同一个目录下,那么conftest.py作用于整个目录
 4.1.3 conftest.py文件所在目录必须存在__init__.py文件
 4.1.4 conftest.py文件不能被其他文件导入
 4.1.5 所有同目录测试文件运行前都会执行conftest.py文件

4.2 conftest结合fixture的使用
 conftest文件实际应用中需要结合fixture来使用,那么fixture中参数scope也适用conftest中fixture的特性,这里再说明一下
 1.conftest中fixture的scope参数为session,那么所有的测试文件执行前执行一次
 2.conftest中fixture的scope参数为module,那么每一个测试文件执行前都会执行一次conftest文件中的fixture
 3.conftest中fixture的scope参数为class,那么每一个测试文件中的测试类执行前都会执行一次conftest文件中的fixture
 4.conftest中fixture的scope参数为function,那么所有文件的测试用例执行前都会执行一次conftest文件中的fixture
pytest.fixture(scope="",autouse=True)

4.3 测试固件之addfinalizer函数
通过request.addfinalizer()的方式实现“teardown”

conftest.py文件

@pytest.fixture(scope="function", autouse=True)
def init_test_environment(request):
    print("*"*30+"初始化setup操作"+"*"*30)
    # 环境、数据销毁--->teardown
    def fin():
        print("*"*30+"销毁teardown操作"+"*"*30)
    request.addfinalizer(fin)

test_fixture.py文件按

def test_fixture():
    print("hello world!")

执行pytest -sv test_fixture.py命令,输出结果:

test_fixture.py::test_fixture ******************************初始化setup操作******************************
hello world!
PASSED******************************销毁teardown操作******************************

在固件中传入request参数,并且定义了一个内置函数fin,也称终结函数,最后将定义的内置函数添加到request的addfinalizer中,addfinalizer支持注册多个终结函数
4.4 调用fixture的两种方法
  4.1.1 fixture可以当做参数传入:函数或类里面方法直接传fixture的函数参数名称
  定义fixture跟定义普通函数差不多,唯一区别就是在函数上加个装饰器@pytest.fixture(),fixture命名不要以test开头,跟用例区分开。fixture是有返回值的,没有返回值默认为None。用例调用fixture的返回值,直接就是把fixture的函数名称当做变量名称

conftest.py文件

@pytest.fixture()
def get_token():
    accesstoken = '197ce8083c38467f'
    return accesstoken

test_fixture.py

def test_fixture(get_token):
    print(get_token)
    print("hello world!")

执行pytest -sv test_fixture.py命令,输出结果:

197ce8083c38467f
hello world!

  4.1.2 使用装饰器@pytest.mark.usefixtures()修饰需要运行的用例

import pytest

@pytest.fixture()
def del_data():
    print("\n--del操作--")

@pytest.mark.usefixtures("del_data")
def test_add_data():
    print("--add操作--")

执行pytest -sv test_fixture.py命令,输出结果:

test_fixture.py::test_add_data
--del操作--
--add操作--
PASSED

  注意: 可以叠加使用usefixtures:如果函数或类里面方法需要多个fixture,可以使用@pytest.mark.usefixture()进行叠加。注意叠加顺序,先执行的放底层,后执行的放上层。

import pytest

@pytest.fixture()
def list_data():
    print("\n--list操作--")

@pytest.fixture()
def del_data():
    print("--del操作--")

@pytest.mark.usefixtures("del_data")
@pytest.mark.usefixtures("list_data")
def test_add_data():
    print("--add操作--")

执行pytest -sv test_fixture.py命令,输出结果:

TestPluginRun.py::test_add_data
--list操作--
--del操作--
--add操作--
PASSED

  使用@pytest.mark.usefixtures与将fixture当做参数传入区别
  ① 当fixture有返回值时,那么使用装饰器@pytest.mark.usefixtures就无法获取到返回值,只能将参数名称直接当参数传入
  ② 当fixture无返回值时,使用两种方式都可以。

五、pytest之mark的使用,实现定制化运行测试用例

背景:有时候我们只需执行部分测试用例,比如从用例集当中挑选 smoke 测试,要怎么做呢?
通过装饰器@pytest.mark.smoke,smoke 是可以自定义的,运行时加上命令‘-m=smoke’,pytest 就会挑选带有装饰器的类或函数运行。

5.1 pytest.mark.xx的作用、使用方法及标记范围:
 5.1.1 注册标签名,用于测试类或测试用例分类
 5.1.2 在测试用例/测试类前面加上:@pytest.mark.标签名
  一个测试函数或测试类允许有多个标记
  一个标记也可以标记多个函数或测试类
  运行参数 pytest - m 标记名
 5.1.3打标记范围:测试用例、测试类、模块文件

5.2 定制化运行测试用例

class Test1(object):

    def test_01(self):
        print("This is test01")

    def test_02(self):
        print("This is test02")

class Test2(object):

    def test_03(self):
        print("This is test03")

    def test_04(self):
        print("This is test04")

 5.2.1 只执行Test1 的测试用例,那么执行命令:

pytest -sq test_fixture.py::Test1

 5.2.2 只想执行Test2中的test_03,那么执行命令:

pytest -sq test_fixture.py::Test2::test_03

 5.2.3 支持多个条件:如果既想运行Test1中的test_02,又想运行Test2的test_04,那么执行命令:

pytest -sq test_fixture.py::Test1::test_02 test_fixture.py::Test2::test_04

 总结:需要运行某个模块某个类的某个方法:命令格式为:pytest -sv [模块名] :: [测试类名称] :: [测试函数名称],中间使用 “::”
分隔,"[]"为可选
 5.2.4 根据测试函数或测试类名称进行筛选 -k参数
执行测试函数或测试类名称包含“2”的测试用例,命令如下:

pytest  -sq  -k  2   test_fixture.py 

六、配置文件pytest.ini

pytest里都有哪些非测试文件?
  1、pytest.ini:pytest的主配置文件,可以改变pytest的默认行为,有很多的配置选项,读取配置信息,按指定的方式去运行
  2、conftest.py:是本地的插件库,其中的hook函数和fixture将作用于该文件所在的目录以及所有的子目录
  3、init.py:每个测试子目录都包含该文件时,那么在多个测试目录中可以出现同名测试文件

  6.1 addopts
  作用:addopts参数可以更改默认命令行选项,当我们在cmd输入一堆指令去执行用例的时候,就可以用该参数代替了,省去重复性的敲命令工作

[pytest]
# 命令行参数----空格分隔,可添加多个命令行参数 -所有参数均为插件包的参数
addopts = -sv --reruns=2 --reruns-delay=1 --html=../report/user.html --junitxml=../report/user.xml

加了addopts之后,在cmd中只需要敲pytest就可以了

  6.2 注册标记来防范拼写错误

import pytest

@pytest.mark.smoke
def test_markers_01():
    print("First smoke test case")


@pytest.mark.smoke
def test_markers_02():
    print("Second smoke test case")

@pytest.mark.smokee
def test_markers_03():
    print("Third smoke test case")

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

[pytest]
log_cli = True
addopts = -s ../Test_teachMS_API --reruns=2 --reruns-delay=1 --alluredir ../report/tmp/
markers=
    smoke:this is smoke test

通过命令查看:

pytest --markers
# 没有注册的标记不会出现在--markers列表里,如果使用--strict选项,遇到拼写错误的标记或未被注册的标记就会报错

没有注册的标记不会出现在–markers列表里
在这里插入图片描述
使用–strict选项,遇到拼写错误的标记或未被注册的标记就会报错

[pytest]
log_cli = True
addopts = -s ../Test_teachMS_API --reruns=2 --reruns-delay=1 --alluredir ../report/tmp/  --strict
markers=
    smoke:this is smoke test

在这里插入图片描述

  6.3 log_cli
  作用:控制台实时输出日志
  格式:log_cli=True 或False(默认),或者log_cli=1 或 0

log_cli配置

[pytest]
log_cli = True
# 命令行参数----空格分隔,可添加多个命令行参数 -所有参数均为插件包的参数
addopts = -sv --reruns=2 --reruns-delay=1 --html=../report/user.html --junitxml=../report/user.xml --alluredir ../report/allurereport/

log_cli = True 的输出结果

test_course.py::Test_CourseAPI::test_add_course[data0] PASSED       [  4%]
test_course.py::Test_CourseAPI::test_add_course[data1] PASSED       [  8%]
test_course.py::Test_CourseAPI::test_add_course[data2] PASSED       [ 13%]
test_course.py::Test_CourseAPI::test_add_course[data3] PASSED       [ 17%]

log_cli = True 可以清晰看到哪个模块下的哪个类下的哪个测试用例是否passed还是failed

  6.4 指定pytest忽略某些目录
  pytest执行用例搜索时,会递归遍历所有子目录,包括某些你明知没必要遍历的目录,可以使用norecursedirs缩小pytest的搜索范围,并且可以重用testpaths指定搜索目录

[pytest]
log_cli = True
addopts = -s ../Test_teachMS_API --reruns=2 --reruns-delay=1 --alluredir ../report/tmp/
markers=
    smoke:this is smoke test
norecursedirs = .*
testpaths = ../Test_teachMS_API

七、 Allure报告

7.1 Allure环境搭建
 7.1.1 下载allure-2.7.0.zip,将其解压至一个文件目录中,找到解压目录的bin目录,将其添加至Path环境变量中
 7.1.2 pip install pytest-allure —>定制化操作

7.2 Allure报告

pytest  --alluredir ../report/tmp/
# 第一种生成测试报告
allure generate ../report/tmp -o ../report/allurereport --clean
# 第二种查看测试报告:启动allure服务查看测试报告:
allure serve ../report/tmp

7.3 Allure报告优化
7.3.1 feature:测试用例特性(主要功能模块)
 使用方法:@allure.feature(),一般标注在类上
7.3.2 story:feature功能模块下的分支功能
 使用方法:@allure.story(),一般标注在测试用例方法上
7.3.3 severity:测试用例的严重级别
 使用方法:@allure.severity(allure.severity_level.CRITICAL)
blocker级别:中断缺陷(程序执行过程中,中间程序错误,无法执行下一步操作)
critical级别:严重缺陷( 影响主要流程执行,一般是重要功能)
normal级别:普通缺陷
minor级别:次要缺陷(界面错误与UI需求不符)
trivial级别:轻微缺陷(必输项无提示,或者提示不规范)
7.3.4 step:测试用例的步骤
 使用方法:@allure.step() 只能以装饰器的形式放在类或者方法上面
       with allure.step(): 可以放在测试用例方法里面,但测试步骤的代码需要被该语句包含
7.3.5 attach:用于向测试报告中添加一些附加信息,通常是一些测试数据信息
 使用方法:allure.attach(body, name, attachment_type, extension)
 body:要写入文件的原始内容
 name : 包含文件名的字符串
 attachment_type: 其中一个allure.attachment_type值
 extension:提供的将用作创建文件的扩展名
7.3.6 link/issue/testcase:链接
 主要是为了将allure报告和测试管理系统集成,可以更快速的跳转到公司内部地址
 @allure.issue(“BUG号:123”) # 问题表识,关联标识已有的问题,可为一个url链接地址
 @allure.issue()
 @allure.testcase(“用例名:测试字符串相等”) # 用例标识,关联标识用例,可为一个url链接地址
三个装饰器的源码分析:

def link(url, link_type=LinkType.LINK, name=None):
    return safely(plugin_manager.hook.decorate_as_link(url=url, link_type=link_type, name=name))


def issue(url, name=None):
    return link(url, link_type=LinkType.ISSUE, name=name)


def testcase(url, name=None):
    return link(url, link_type=LinkType.TEST_CASE, name=name)
 
 """
 issue()和testcase()其实调用的也是link(),只是link_type不一样
 必传参数 url:跳转的链接
 可选参数 name:显示在allure报告的名字,如果不传就是显示完整的链接;建议传!!不然可读性不高
 可以理解成:三个方法是一样的,我们都提供跳转链接和名字,只是链接的type不一样,最终显示出来的样式  不一样而已
 """

7.3.7 description:用例描述
 使用方法:@allure.description():提供描述字符串的装饰器
      @allure.description_html() 提供一些HTML在测试用例的描述部分

八、插件

  失败重试:pytest-rerunfailures
  安装该插件后,直接在pytest.ini文件中addopts = 添加参数–reruns n (n:为重试的次数) --reruns-delay=d(d:每隔几秒重试)
  控制被测试函数执行的顺序:pytest-ordering
  安装该插件后,通过修饰符写在函数上一行@pytest.mark.run(order=3)

# -*- coding:utf-8 -*-
#@file:TestPluginRun.py
import pytest

class Test_Add:
    @pytest.mark.run(order=2)
    def test_add_01(self):
        print("-----test_add_01-----")
        pass

    @pytest.mark.run(order=1)
    def test_add_02(self):
        print("-----test_add_02-----")
        pass


class Test_Sub:

    def test_sub_01(self):
        print("-----test_sub_01-----")
        pass

    def test_sub_02(self):
        print("-----test_sub_02-----")
        pass

命令行输入:pytest -v TestPluginRun.py,输出结果为:

TestPluginRun.py::Test_Add::test_add_02 PASSED  [ 25%]
TestPluginRun.py::Test_Add::test_add_01 PASSED  [ 50%]
TestPluginRun.py::Test_Sub::test_sub_01 PASSED  [ 75%]
TestPluginRun.py::Test_Sub::test_sub_02 PASSED  [100%]

1、标记于被测试函数, @pytest.mark.run(order=x)
2、根据order传入的参数来解决运行顺序
3、order值全为正数或负数时,值越小优先级越高
4、正负数同时存在时,正数优先极高
5、已标记和未标记的函数,已标记的函数优先极高

  pytest-sugar:pytest-sugar改变了pytest的默认外观,增加了一个进度条,并立即显示失败的测试。它不需要配置,只需点击安装pytest-sugar,用pytest运行你的测试,可获得更漂亮,更有用的输出

  pytest-cov:增加了对pytest的覆盖支持,以显示哪些代码行已经测试,哪些没有。它还将包括项目的测试覆盖率

  pytest-picked:运行基于你已修改但尚未提交给git的代码的测试。安装库并使用pytest-picked运行测试,以仅测试自上次提交以来已更改的文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值