pytest测试框架 (allure测试报告、数据驱动ddt、夹具【fixture,yield】)

测试框架 : unittest pytest,技术栈,提供了表示测试用例,发现测试用例,执行测试用例。生成测试结果报告…
自动化测试框架:利用好技术栈
【python+loguru+openpyxl+pytest+allure+requests】+【代码封装思想
+数据分离思想+代码分层思想+数据驱动思想】

1、手工测试和自动化测试的思路流程: --任何语言都是一样的 通用的

手工执行测试:
熟悉业务-- 写用例(分模块)-- 执行用例并记录 -生成本轮的测试报告

自动化测试:
熟悉业务 – 写用例(手工用例转化为自动化测试用例)-- 用代码表达用例 (代码写出用例) – 代码收集测试用例-- 代码执行测试用例 – 代码生成测试报告。

自动化的思路基本是跟手工测试一样的,建立在手工测试基础上的一种更高效
率的进阶和升华。

2、自动化测试搭建有哪些框架

unittest : 内置库,有二次开发的库,比如unittestreport,不太灵活 不太智能。

pytest: 第三方库,现在用的更多。-- 主流 只能需要安装:pip install pytest
安装完可以直接 使用pytest 运行用例

pytest和unittest都是单元测试框架,可以用来编写测试用例,收集用例,运行用例,生成报告,实现前后置。

pytest编写测试用例 【2种方式】

1、pytest用例语法规则:

1)测试函数的形式写: 函数名要以test_开头

import  random

# pytest用例
import pytest

@pytest.mark.p2
@pytest.mark.p1
def test_demo():
    assert 1 == 2


def test_dv():
    return  10/0
  1. 测试类的形式写: 类名要以Test开头,类里面的方法也要以test_开头 才会被识别为pytest的用例。
import  random

# 普通函数
import pytest


def ger_ran():
    num = random.randint(1,30)
    return num

class TestDemo:
    @pytest.mark.p1
    def test_01(self):
        """第一条用例"""
        assert ger_ran() > 15

    @pytest.mark.p2
    def test_02(self):
        """第二条用例"""
        assert ger_ran() < 20

2、运行pytest用例:

1) 运行的单个模块用例,右键运行 点击三角符号运行

2) 完整项目框架里每个模块单独用一个py文件管理,需要收集所有模块的用例,一起执行。

pytest智能自动收集所有用例:pytest.main(): 自动在这个文件所在目录收集符合命名规则的文件
原理: 不同模块 不同目录的 主要符合命名规则的 都会拿过来执行。

  • rootdir 范围里找: main所在目录项目的根目录

  • 文件名字: test_开头,或者 _test开头 【 但是一般推荐使用第一种,符合大家的习惯】

  • 用例: 测试用例名字 test开头 | Test开头的类+ test_开头方法函名字

  • 跟文件夹的名字 无关。可以放在文件夹里,且文件夹的名字无所谓。

"""
完整项目框架里每个模块单独用一个py文件管理,需要收集所有模块的用例,一起执行。
* pytest智能自动收集所有用例:pytest.main(): 自动在这个文件[run.py]所在目录收集符合命名规则的文件
* 原理: 不同模块 不同目录的 主要符合命名规则的 都会拿过来执行。
    * - rootdir 范围里找: main所在目录项目的根目录
    * - 文件名字: test_开头,或者 _test开头 【 但是一般推荐使用第一种,符合大家的习惯】
    * - 用例: 测试用例名字 test开头 | Test开头的类+ test_开头方法函名字
    * - 跟文件夹的名字 无关。可以放在文件夹里,且文件夹的名字无所谓。

问题:如果不想要收集所有的呢? 执行部分或者某些用例怎么实现?-- 测试用例过滤
* 方式一: 修改文件和用例方法的名字 --最简单最直观
* 方式二: 指定目录和文件执行 ,加参数控制
* 方式三: 加标签【类比手工测试用例的优先级: P1 P2 P3 P4 (important critical major) high medium low】, 加参数过滤用例
    * 用例定义的加一个标签 : 用装饰器形式:@pytest.mark.p2
    * 执行的时候 加参数 -m 标签
    - 注意: 直接加标签 会有一个警告信息 但是不影响运行不报错; 但是不想出现这个警告 可以提前注册一下标签。
    - 注册方法: pytest.ini配置文件在项目的根目录下面。 注册标签名。
    [pytest]
markers=
    p1
    p2
    p3
    high

pytest.main([参数1,参数2,...])


allure报告失败了 原因2种:
1- 测试用例本身断言失败 : 预期结果 和执行结果不一样,可能是bug ,产品的缺陷;
2- 用例本身报错了: 代码设计问题

报告关注用例在执行用时: 面试题: 接口自动化总共覆盖了多少个用例?总共执行花了多少时间? == 【单个接口执行时间 * 总共用例数量】
- 单个接口用例执行 几十ms
- 总共用例数量: 手工用例 5000个,接口自动化测试覆盖率【回归测试】90%+ == 自动化用例数量:4500个
- 4500 * 200ms = 900000ms --> 900s ,15min
- 如果是业务流接口【多接口关联】 1s-10s
- UI自动化更慢: 页面加载速度有关。 1个用例几十s 。

完整的项目流程 集成储持续集成Jenkins 一起工作: 构建历史查看到每次构建的结果报错。-发送邮件。
os执行限制比较多,不推荐用。
"""

from tools.handle_path import log_path

# 存储日志文件代码
import pytest
from loguru import logger
from tools.handle_path import log_path

logger.add(sink=log_path,
           encoding="UTF8",
           level="INFO",
           rotation="10MB",
           retention= 20)

# 运行所有的用例:
# pytest.main([r"testcases\test_demo2_pytest.py"])  # 用例过滤  具体某个路径
# pytest.main([r"testcases\test_demo2_pytest.py","-m p1"])  # 用例过滤 标签过滤用例一个标签
# pytest.main(["-v",r"testcases\test_demo2_pytest.py","-m p1 or p2"])  # 用例过滤 标签过滤用例多个标签

pytest.main(["-v","--alluredir=outputs/allure_report","--clean-alluredir"])
# -v 参数详细信息显示


注意事项: 如果pytest没有识别出来,或者之前有运行过unittest框架用例,需要做如下配置: 【pycharm是自动检测 不需要额外的配置的。如果不行可以配置一下】

1、安装好之后,可以配置为pytest执行方式: file–setting–tools–Pythoniintegrated tools – Testing :修改为pytest; 如果用unittest 就选unitte

在这里插入图片描述
2、如果之前运行过unittest的,记得去右上角的运行按钮–edit configuration删除所有的运行记录。配置好了之后,就会自动识别为pytest的用例。-有个绿色的小按钮,可以运行和debug。
在这里插入图片描述
在这里插入图片描述
问题:如果不想要收集所有的呢? 执行部分或者某些用例怎么实现?
方式一: 修改文件和用例方法的名字
方式二: 指定目录和文件执行 ,加参数控制
方式三: 加标签【类比手工测试用例的优先级: P1 P2 P3 P4(important critical major) high medium low】, 加参数过滤用例用例定义的加一个标签 : 用装饰器形式:@pytest.mark.p2执行的时候 加参数 -m 标签

赛选用例参考文章

pytest生成测试报告 – allure 插件

这是一个开源的 独立的展示测试报告的工具,需要各种语言的框架去执行测试用例,生成allure可以
解析的结果文件,从而生成allure的测试报告。

官方文档: https://docs.qameta.io/allure-report/

跨平台的 python java go js等语言都支持,官方文档里有显示;

对于python来说,支持pytest,还有两个两个,不那么主流;unittest不支持

1)安装allure工具:

a、安装jdk
b、下载allure,配置环境变量
  • 解压到任意目录,建议目录不要在C盘 不要太深;

  • 进入allure解压后的目录,找到bin目录,然后把bin目录配置为path 的环境变量

    • path添加如下目录即可:如 D:\allure\allure-2.23.1\bin
  • cmd里检查环境变量是否配置成功: 输入allure --version 看是否出现版本。

c、pytest要生成allure解析的测试结果文件,安装pytest的allure插件

【Python的第三方库】: pip install allure-pytest==2.11.1 或者pycharm安装;

  • 运行用例的时候就会自动生成结果文件: 使用pytest运行的参数:加一个参数:–alluredir=相对于rootdir的目录

  • 在当前目录下就会生成一个reports目录下的allure目录,然后结果文件都生成在里面。-json文件给allure看的

  • 这些只是结果文件,还没有生成报告,接下来要allure来解析这些结果文件 生成allure的报告 --HTML页面给人看的

  • 使用allure命令生成报告:

    • cmd里或者terminal里,跳转到rootdir目录;
    • 运行命令: allure serve reports_allure === serve后面跟的是自己定义的结果文件的目录,相对于rootdir的目录;
import pytest
#加一个参数:--alluredir=相对于rootdir的目录
pytest.main(["--alluredir=reports_allure"])
# 每次执行一次就会又重复生成结果文件不会清楚;会影响报告结果,所以
需要加一个清除的参数:
pytest.main(["-v","--alluredir=reports/allure",
 "--clean-alluredir"]) # 清除上次执行的结果文件

pytest的数据驱动: data deriven test,是一种设计思想;

在什么场景下可以使用这种思想:
1)有一个通用的流程:不同的测试数据使用的同一个方法执行;
2) 多组数据,都走这个流程;不同的数据使用同一个方法 驱动不同的结果。

ddt的语法:

  • @pytest.mark.parametrize 是个装饰器,里面两个数据: data 和datas
    意思就是: 将datas里每个成员传递给data这个变量接受;
  • data注意要加引号,虽然是个变量 但是要加引号
  • 后面的用例里的参数data 都是必须要要跟这个装饰器里的data保持一致。
  • 运行结果:
    • 会运行3条测试用例: 几条数据就运行几条测试用例
    • 然后就算前面的断言失败了,也依然会允许后续的用例。
第一种写法:函数形式的用例:
@pytest.mark.parametrize("变量名",装用例的列表/字典/元组)
def test_login(变量名): # 注意,变量名跟装饰器里的变量名保持一致
 res = login(变量名["name"],变量名["pwd"])
 assert res == 变量名["expect"]

范例:

import pytest
from loguru import logger
# login流程
def login(username,password):
    if username != "admin":
        return "用户名错误"
    if password != "123456":
        return "密码错误"
    return "登录成功"

# 准备测试用例数据 - 正常 异常数据
datas = [{"name":"admin","pwd":"123456","expected":"登录成功1"},
         {"name":"lemon","pwd":"123456","expected":"用户名错误"},
         {"name":"admin","pwd":"1234","expected":"密码错误"}]

# 数据驱动实现
@pytest.mark.parametrize("data",datas)  # 依次取到datas里面的每一个元素,传递给data接受
def test_login(data):  # 把data变量传递给函数的参数 :data就是每一条用例的字典。
    username = data["name"] # data : {"name":"admin","pwd":"123456","expected":"登录成功1"}
    password = data["pwd"]
    result = login(username,password)
    logger.info("用例执行开始..")
    # 断言
    assert result == data["expected"]

第二种: 类形式的测试用例:–写在类上面也可以,就会给类下面每个方法都
进行数据驱动。

class TestDemoC:
 @pytest.mark.parametrize("变量名",装用例的列表/字典/元组)
 def test_login(变量名): # 注意,变量名跟装饰器里的变量名保持一致
 res = login(变量名["name"],变量名["pwd"])
 assert res == 变量名["expect"]
或者
@pytest.mark.parametrize("变量名",装用例的列表/字典/元组)
class TestDemoC:
 def test_login1(变量名): # 注意,变量名跟装饰器里的变量名保持一致
 res = login(变量名["name"],变量名["pwd"])
 assert res == 变量名["expect"]
 def test_login2(变量名): # 注意,变量名跟装饰器里的变量名保持一致
 res = login(变量名["name"],变量名["pwd"])
 assert res == 变量名["expect"]

范例:

import pytest
from loguru import logger
# login流程
def login(username,password):
    if username != "admin":
        return "用户名错误"
    if password != "123456":
        return "密码错误"
    return "登录成功"

# 准备测试用例数据 - 正常 异常数据
datas = [{"name":"admin","pwd":"123456","expected":"登录成功1"},
         {"name":"lemon","pwd":"123456","expected":"用户名错误"},
         {"name":"admin","pwd":"1234","expected":"密码错误"}]

# 数据驱动实现 --类形式
# class TestDDT:
#     @pytest.mark.parametrize("data",datas)  # 依次取到datas里面的每一个元素,传递给data接受
#     def test_login(self,data):  # 把data变量传递给函数的参数 :data就是每一条用例的字典。
#         username = data["name"] # data : {"name":"admin","pwd":"123456","expected":"登录成功1"}
#         password = data["pwd"]
#         result = login(username,password)
#         logger.info("用例执行开始..")
#         # 断言
#         assert result == data["expected"]

@pytest.mark.parametrize("data", datas)  # 依次取到datas里面的每一个元素,传递给data接受
class TestDDT:
    def test_login(self,data):  # 把data变量传递给函数的参数 :data就是每一条用例的字典。
        username = data["name"] # data : {"name":"admin","pwd":"123456","expected":"登录成功1"}
        password = data["pwd"]
        result = login(username,password)
        logger.info("用例执行开始..")
        # 断言
        assert result == data["expected"]

    def test_login2(self,data):  # 把data变量传递给函数的参数 :data就是每一条用例的字典。
        username = data["name"] # data : {"name":"admin","pwd":"123456","expected":"登录成功1"}
        password = data["pwd"]
        result = login(username,password)
        logger.info("用例执行开始..")
        # 断言
        assert result == data["expected"]

pytest的前置后置-夹具 fixture

  • 有些内容是在每个用例我执行之前都要运行操作:
    • 接口: 购物车模块先登录 --登录结果
      UI: 每次用例 打开浏览器 --driver
  • 有些内容再每个用例之后都要运行操作:
    • 接口: 数据清除
    • UI:关闭浏览器

叫做用例的前置 和后置。 pytest测试框架 统一叫做夹具。fixture。

  • 定义夹具语法:
    在函数前面加一个装饰器: @pytest.fixture, 申明后就是夹具。
@pytest.fixture # 申明它是一个fixture的函数
def setup_teardown():
 print("我是前置代码..")
 print("我是后置代码..")

在测试用例里调用前置后置函数:

def test_demo(setup_teardown):
 assert 1 == 1
yield: 前置和后置的划分 ,还可以设置夹具的返回值
import pytest
@pytest.fixture # 申明它是一个fixture的函数
def setup_teardown():
 print("我是前置代码..")
 yield # 作为前置和后置的划分线,后面还可以定义夹具的返回值。
 print("我是后置代码..")
夹具的conftest 共享:
  • 如果要直接实现多个模块共享夹具,可以用conftest。
    • 1、创建一个conftest.py文件,把夹具代码复制进去
      • 注意文件名不能改,只能是这个名字 conftest
    • 2、调用conftest里的夹具,不需要导入conftest.py模块,可以自动查找fixture。
      • 另外一个Python文件的测试用例直接调用这个夹具,不需要导入,直接调用可以运行
    • 3、conftest.py的作用范围: conftest.py所在文件夹下面的所有用例。一般放在根目录下 或者testcases目录下都可以。
      • 也有可能在同一个项目中不同的目录下创建conftest.py文件 这也是可以的;那么也是在它目录下生效;
      • 超出了范围就会报错:E fixture ‘class_setup_teardown’ not found
        conftest.py内容如下
"""
夹具的共享: 夹具写在conftest文件里, 名字固定。 在这个范围内容的用例都可以自动发现里面夹具。【不需要导入】
- 在conftest的所在目录下的所有文件都可以发现他。 == 一般会放在跟目录 + 测试用例目录
- 如果超过范围:  fixture 'setup_teardown' not found。

问题: 自己的模块有夹具,conftest也有 ,多个conftest在本目录和上级目录,优先用哪个?
- 大原则: 就近原则。
- 名字不一样,还以名字区分。
- 名字一样: 就近原则 ,优先用自己的夹具。
    - 1) 如果用例所在的测试文件中 找fixture,找到了就用
    - 2)如果1)中没有找到,就去当前文件所在目录下contest.py找,找到了就用
    - 3)如果2)中没有找到,就去【当前文件所在目录的上一级目录】下contest.py找,找到了就用
    - 4)一直摘到rootdir下面截止,没有找到 就报错。

夹具作用域类型:
- 函数级别:function: 函数前后执行  ==默认的
- 类级别的: class : 类前后执行   不会每个方法都执行一次
  - 调用家眷写在第一个方法里。 第二个会有问题。
- module级别:
- session:会话级别

"""
import pytest
@pytest.fixture(scope="class")  # 一定要申明夹具
def setup_teardown():
    print("我是前置代码...")
    yield "token_value:asjkhdaksjhda","user_id:11032"
    print("我是后置代码...")

当用例的模块也有夹具和conftest优先会用哪个夹具?–就近原则

  • 名字不一样,按照名字区分
  • 名字一样,就近原则,优先用自己的夹具。
    • 1)如果用例所在的测试文件中 找fixture,找到了就用
    • 2)如果1)中没有找到,就去当前文件所在目录下contest.py找,找到了就用
    • 3)如果2)中没有找到,就去【当前文件所在目录的上一级目录】下contest.py找,找到了就用
    • 4)一直摘到rootdir下面截止,没有找到 就报错。

3、pytest的特色总结:

  • 1) 可以直接使用assert断言,并且自动识别预期和实际结果,报错信息详细显示 : == 这里区别于unittest 的assertEqual 的断言方法
  • 2)可以自动发现测试模块和测试函数里的测试用例,并自动收集执行;
  • 3)有非常丰富的第三方插件,allure报告
  • 4)pytest的数据驱动灵活
    1. pytest的夹具非常灵活且功能丰富
    • conftest自动发现,不用导入共享
    • 可以设置夹具返回值
    • 前置后置写在一个夹具里
  • 6)pytest还有筛选的功能:过滤某些用例执行和不执行-标签功能 【结合手工测试用例优先级 】
    冒烟测试: P1
    回归测试: P1 P2
  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pytest测试框架中的fixture是为了在测试过程中提供预定义的测试数据测试环境设置。在给定的引用中,我们可以看到三个使用fixture的示例。 引用中的示例展示了一个带有fixture测试类TestCase。其中的test4方法接受一个名为test1的fixture作为参数。在这个方法中,我们可以看到对test1的使用,并且验证了test1的值与预期值sex相等。 引用中的示例展示了一个带有fixture测试函数test2。在这个函数中,我们可以看到test1作为一个fixture被注入到test2函数中,然后我们可以验证test1的值与预期值'leo'相等。 引用中的示例也展示了一个带有fixture测试函数test3。与前面的示例类似,test3函数接受一个名为test1的fixture作为参数,并在函数中使用test1并验证其值与预期值name相等。 总之,pytestfixture可以用于提供测试数据或设置测试环境,并且可以在测试函数或测试类中使用。它可以帮助我们更方便地编写和组织测试代码。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [pytest框架fixture详细使用](https://blog.csdn.net/king_liuhui/article/details/122819352)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值