1 Star 0 Fork 44

panwumei / pytest-yaml-yoyo

forked from yoyo / pytest-yaml-yoyo 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

1 环境与准备

1.1 说明

基于 httprunner 框架的用例结构,我自己开发了一个 pytest + yaml 的框架,那么是不是重复造轮子呢? 不可否认 httprunner 框架设计非常优秀,但是也有缺点,httprunner3.x 的版本虽然也是基于 pytest 框架设计,结合 yaml 执行用例,但是会生成一个py文件去执行。 在辅助函数的引用也很局限,只能获取函数的返回值,不能在 yaml 中对返回值重新二次取值。 那么我的这个框架,就是为了解决这些痛点。。。。 完整视频教程地址https://study.163.com/course/courseMain.htm?courseId=1213419817&share=2&shareId=480000002230338 (视频收费:包含 pytest + yaml 框架开发教学和使用教学, 买课程的学员可以用新版本更多强大功能,咨询wx:283340479)

本插件可以实现以下优势:

  • 1、基于 pytest 框架安装插件即可使用,环境非常简单
  • 2、只需要写 yaml 文件用例即可运行,使用 pytest 运行的命令
  • 3、yaml 文件中支持定义变量 与 引用变量
  • 4、extract 功能实现多个接口步骤的参数关联
  • 5、全局仅 base_url 功能,yaml 中写相对路径即可
  • 6、全局仅登录一次,在用例中自动在请求头部添加 Authorization token认证
  • 7、用例参数化 parameters 功能实现
  • 8、yaml 中调用 fixture 功能实现
  • 9、yaml 中支持自定义函数调用
  • 10、yaml 中调用 hooks 功能(sign签名与加解密)
  • 11、用例分层机制:API和用例层
  • 12、支持 logging 日志
  • 13、支持 allure 报告
  • 14、支持 mysql 数据库增删改查
  • 15、支持钉钉机器人通知测试结果和 allure 报告地址
  • 16、支持生成随机测试数据,如字符串,姓名,手机号,邮箱等
  • 17、根据 swagger.json 自动生成 yaml 文件接口用例
  • 18、支持全局代理配置
  • 19、全局配置 env 多套环境切换
  • 20、CLI 执行用例,完全适配持续集成 CI/CD 流程
  • 21、export 导出全局变量 (VIP 付费功能)
  • 22、飞书群/企业微信群机器人通知 (VIP 付费功能)
  • 23、引用变量支持过滤器的使用 (VIP 付费功能)
  • 24、mark 对用例标记功能 (VIP 付费功能)
  • 25、runtime 断言用例运行时长 (VIP 付费功能)
  • 26、录制自动生成 yaml 用例 (VIP 付费功能)
  • 27、mock 拦截请求自定义返回 (VIP 付费功能)
  • 28、parameters参数化支持模块级别和用例级别,支持笛卡尔积 (VIP 付费功能)
  • 29、新增仅收集用例失败错误信息和 log 日志(VIP 付费功能)
  • 30、allure报告自定义内容 (VIP 付费功能)
  • 31、redis 数据库支持 (VIP 付费功能)
  • 32、websocket 协议支持 (VIP 付费功能)

联系我们:

1.2 环境准备

最低版本要求 Python 3.8 版本或以上版本. 目前兼容 python3.8, python3.9, python3.10版本 (低于 python3.8 版本无法安装此插件,低版本python不做适配) Pytest 7.2.0+ 最新版可以有最佳体验

pip 安装插件即可使用,不需要下载源码

pip install pytest-yaml-yoyo

2 快速开始

2.0 快速创建项目demo

使用 --start-project 命令, 帮助初学者快速创建项目 demo 结构, 并自动创建几个简单的用例。

执行以下命令

pytest --start-project

运行日志

(venv) D:\demo\untitled_start>pytest --start-project
create ini file: D:\demo\untitled_start\pytest.ini
create config file: D:\demo\untitled_start\config.py
create file: D:\demo\untitled_start\case_demo
create yaml file: D:\demo\untitled_start\case_demo\test_get.yml
create yaml file: D:\demo\untitled_start\case_demo\test_post.yml
create yaml file: D:\demo\untitled_start\case_demo\test_extract.yml

执行完成会自动生成以下文件

    D:\demo\
    ├── case_demo/
    │   ├── test_extract.yml
    │   ├── test_get.yml
    │   ├── test_post.yml
    ├── config.py
    ├── pytest.ini

test_extract.yml 内容

config:
  name: 参数关联-用例a提取结果给到用例b

test_a:
  name: extract提取结果
  request:
    method: POST
    url: /post
    json:
      username: test
      password: "123456"
  extract:
      url:  body.url
  validate:
  - eq: [status_code, 200]
  - eq: [headers.Server, gunicorn/19.9.0]
  - eq: [$..username, test]
  - eq: [body.json.username, test]

test_b:
  name: 引用上个接口返回
  request:
    method: GET
    url: http://httpbin.org/get
    headers:
      url: ${url}
  validate:
  - eq: [status_code, 200]

自动创建 pytest.ini 文件,并添加 2 个配置参数

[pytest]
log_cli = true
env = test

看到项目结构生成后,仅需执行 pytest 命令即可运行用例

pytest

2.1 第一个hello world

yaml 用例编写规则,跟 pytest 识别默认规则一样,必须是 test 开头的,以 .yml 结尾的文件才会被识别

注意: v1.1.4 之后的新版本可以识别 .yml 和 .yaml 2种后缀格式, 并且是 test 开头或者 test 结尾的文件 四种文件都能被识别成用例:test*.yml 、 test*.yaml 、*test.yml、 *test.yaml

新建一个test_hello.yml文件

config:
  name: yy

teststeps:
-
  name: demo
  print: hello world

用例整体结构延续了 httprunner 框架的用例结果,主要是为了大家快速上手,减少新的规则学习

  • config 是非必须的里面有 name 用例名称,base_url 和 variables 是可选的
  • teststeps 是非必须,用例的步骤,用例步骤是一个array 数组类型,可以有多个步骤

从上面的运行可以看出,request 不是必须的,我们可以直接调用python内置函数print 去打印一些内容了。

2.2 一个简单的 http 请求

http://www.example.com/ get 请求示例 test_get_demo.yml

config:
  name: get

teststeps:
-
  name: get
  request:
    method: GET
    url: http://www.example.com/
  validate:
    - eq: [status_code, 200]

命令行输入 pytest 后直接运行

>pytest
======================= test session starts =======================
platform win32 -- Python 3.8.5, pytest-7.2.0, pluggy-1.0.0
rootdir: D:\demo\yaml_yoyo
plugins: yaml-yoyo-1.0.1
collected 2 items                                                  

test_get_demo.yml .                                          [ 50%]
test_hello.yml .                                             [100%]

======================== 2 passed in 0.49s ========================

运行规则规则跟 pytest 完全一致,使用 pytest 的命令行运行用例

2.3 一个简单的 post 请求

test_post_demo.yml

config:
  name: post示例

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$..username, test]
    - eq: [body.json.username, test]

2.4 validate校验

比如返回的response内容

HTTP/1.1 200 OK
Date: Wed, 23 Nov 2022 06:26:25 GMT
Content-Type: application/json
Content-Length: 483
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "args": {}, 
  "data": "{\r\n    \"username\": \"test\",\r\n    \"password\": \"123456\"\r\n}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Content-Length": "55", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "Fiddler", 
    "X-Amzn-Trace-Id": "Root=1-637dbd11-7d9943ba1fb93a9331f6cf8d"
  }, 
  "json": {
    "password": "123456", 
    "username": "test"
  }, 
  "origin": "198.187.30.113", 
  "url": "http://httpbin.org/post"
}

校验可以支持 response 取值对象:status_code, url, ok, headers, cookies, text, json, encoding 其中返回的是 json 格式,那么可以支持

  • jmespath 取值语法: body.keyname.keyname
  • jsonpath 语法: $..keyname
  • re 正则语法: xx(.+?)xxx

如果返回的不是 json 格式,那么可以用正则取值

2.5 变量的声明与引用

变量的声明,只支持在config 声明整个yml文件的全局变量(不支持单个step的变量,减少学习成本) 在 httprunner 里面变量引用语法是$user, 引用函数是${function()} 我这里统一改成了一个语法变量引用${var} 和 引用函数${function()} (表面上没多大变量,实际上功能强大了很多,使用了强大的 jinja2 模板引擎) 可以在引用函数后继续对结果操作, 这就解决了很多人提到了函数返回一个 list,如何在 yaml 中取某一个值的问题

config:
  name: post示例
  variables:
    username: test
    password: "123456"

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${username}
      password: ${password}
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$..username, test]
    - eq: [body.json.username, test]

运行结果

3.用例编写

3.1 参数关联

3.1.1 extract 提取接口返回参数关联

在自动化用例中,我们经常会看到有人提问,上一个接口的返回的结果,如何取出来给到下个接口的入参。 我们用 extract 关键字提取接口的返回结果(需要更新v1.0.2版本)。

举个例子 用个post请求http://httpbin.org/post

POST http://httpbin.org/post HTTP/1.1
User-Agent: Fiddler
Host: httpbin.org
Content-Length: 0

HTTP/1.1 200 OK
Date: Thu, 24 Nov 2022 06:18:03 GMT
Content-Type: application/json
Content-Length: 320
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Content-Length": "0", 
    "Host": "httpbin.org", 
    "User-Agent": "Fiddler", 
    "X-Amzn-Trace-Id": "Root=1-637f0c9a-23b419f4180f6b843ba941af"
  }, 
  "json": null, 
  "origin": "66.112.216.24", 
  "url": "http://httpbin.org/post"
}

比如我需要提取返回接口里面的url参数,那么我们用extract 关键字

test_demo.yml 文件示例

config:
  name: post示例

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$..username, test]
    - eq: [body.json.username, test]

3.1.2 引用提取结果

上一个接口提取到了url 变量,接下来在下个接口中引用${url}

config:
  name: post示例

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$..username, test]
    - eq: [body.json.username, test]

-
  name: post
  request:
    method: GET
    url: http://httpbin.org/get
    headers:
      url: ${url}
  validate:
    - eq: [status_code, 200]

于是看到请求报文中引用成功

GET http://httpbin.org/get HTTP/1.1
Host: httpbin.org
User-Agent: python-requests/2.28.1
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
url: http://httpbin.org/post

3.1.3 extract 提取结果二次取值

在yaml中对返回值重新二次取值 对于提取的结果,我想继续取值,比如他是一个字符串,在python中可以用切片取值 那么,在 yaml 中如何实现?

我重新设计的这个框架中,就可以支持python语法,直接用切片取值

headers:
      url: ${url[:4]}

请求报文

GET http://httpbin.org/get HTTP/1.1
Host: httpbin.org
User-Agent: python-requests/2.28.1
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
url: http

extract 取值语法

校验方式可以支持response取值对象:status_code, url, ok, headers, cookies, text, json, encoding 其中返回的是json格式,那么可以支持

  • jmespath 语法: body.json.username
  • jsonpath 语法: $..username
  • re 正则语法:'code: (.+?),'

如果返回的不是json格式,那么可以用正则取值

当一个用例用到多组测试数据的时候,我们必然会用到参数化,接下来看下如何在yaml文件中实现参数化

3.2 全局 Token 管理

我们在使用自动化测试框架的时候,经常会遇到一个需求,希望在全局用例中,仅登录一次,后续所有的用例自动带上请求头部token 或者cookies。

3.2.1 登录fixture 功能

我在 pytest + yaml 框架框架中封装了一个内置 fixture 叫requests_session, 它的作用范围是scope="session",也就是全部session用例会话中仅实例化一次。 现在我只需在conftest 中写一个登录的fixture功能,获取token后添加到requests_session头部

import pytest
"""
全局仅登录一次,获取token,
在请求头部添加Authorization Bearer 认证
内置fixture requests_session
"""




@pytest.fixture(scope="session", autouse=True)
def login_first(requests_session):
    """全局仅一次登录, 更新session请求头部"""
    # 调用登录方法,获得token
    token = "*******************"
    headers = {
        "Authorization": f"Bearer {token}"
    }
    requests_session.headers.update(headers)

上面代码中,我用login() 函数实现登录功能,这里返回一个随机值,主要是为了验证我只调用了一次登录方法 接着我写2个yaml文件(注意,yaml文件中也不需要重复去添加请求头部了

test_get_demo.yml

config:
  name: get

teststeps:
-
  name: get
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

test_post_demo.yml

config:
  name: post示例
  variables:
    username: test
    password: "123456"

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${username}
      password: ${password}
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$..username, test]
    - eq: [body.json.username, test]

3.2.2 运行用例

在命令行中输入pytest运行 抓包看发过去的请求

于是可以看到,在2个用例中都自动带上了请求头部参数。 (登录cookies的使用原理也是一样的,登录后cookies一般会自动保持)

3.2.3 其它需求

那有些接口不需要登录怎么办呢?比如登录和注册的接口,是不需要带上登录token的,那不能一刀切。 我除了默认用到一个requests_session 全局的内置fixture,还预留了2个

  • requests_module: 每个yaml文件中用一次
  • requests_function: 每个用例中用一次

在yaml文件中切换到指定fixture功能,requests_modulerequests_function 后续会实现,先实现大需求,解决大部分人遇到的痛点问题!

3.2.4 requests_module 和 requests_function

那有些接口不需要登录怎么办呢?比如登录和注册的接口,是不需要带上登录token的。

除了默认用到一个requests_session 全局的内置fixture,还预留了2个

  • requests_module: 每个yaml文件中用一个请求会话(会保持cookies)
  • requests_function: 每个用例中用一次,每个用例独立运行,不保持cookies

接下来看下如何在用例中使用test_register.yml

config:
  name: post示例
  fixtures: requests_module

注册1:
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test123
      password: "123456"
  validate:
    - eq: [status_code, 200]


注册2:
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test444
      password: "123456"
  validate:
    - eq: [status_code, 200]

在config 中传入 fixtures参数,requests_module 是每个yaml文件中用一个请求会话(会保持cookies) requests_function 作用是每个用例中用一次,每个用例独立运行,不保持cookies。

3.2.5 自定义 fixtures

pytest 的核心功能是学会灵活使用fixtures, 那么我们的这个插件也是可以支持在用例中调用fixtures功能的。

在conftest.py 文件中写你需要实现的fixture 功能, 设置使用范围为scope="function" 函数级别

import pytest


@pytest.fixture(scope="function")
def demo_fixture():
    print("用例前置操作->do something .....")
    yield
    print("用例后置操作,do something .....")

在 yaml 文件中引用 fixture

config:
  name: post示例
  fixtures: demo_fixture

注册1:
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test123
      password: "123456"
  validate:
    - eq: [status_code, 200]


注册2:
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test444
      password: "123456"
  validate:
    - eq: [status_code, 200]

于是运行结果可以看到,每个用例前后都会执行

collected 2 items                                                                                          

test_f2.yml 用例前置操作->do something .....
.用例后置操作,do something .....
用例前置操作->do something .....
用例后置操作,do something .....

如果想整个yaml 文件中仅运行一次,那么conftest.py 文件中写你需要实现的 fixture 功能, 设置使用范围为scope="module" 模块级别

import pytest


@pytest.fixture(scope="module")
def demo_fixture():
    print("用例前置操作->do something .....")
    yield
    print("用例后置操作,do something .....")

于是看到运行的时候,仅在yaml 文件的全部用例中只执行一次

collected 2 items                                                                                          

test_f2.yml 用例前置操作->do something .....
..用例后置操作,do something .....

3.2.6 多个fixtures的使用

当 yaml 中的用例需要用到多个fixtures时, 支持2种格式

格式一: 逗号隔开

config:
  fixtures: fixture_name1,  fixture_name2

格式二: 用 list

config:
  fixtures: [fixture_name1,  fixture_name2]

requests_module 和 requests_function 内置 fixture 功能在 v1.1.1 版本实现, 版本太低的请及时更新版本。

3.3 parameters 参数化

3.3.1 参数化数据结果

当一个用例用到多组测试数据的时候,我们必然会用到参数化,接下来看下如何在yaml文件中实现参数化 用例参数化的实现,我设计了2种实现方式

参数化方式1:

  config:
     name: post示例
     fixtures: username, password
     parameters:
       - [test1, '123456']
       - [test2, '123456']

参数化方式2:

  config:
     name: post示例
     parameters:
       - {"username": "test1", "password": "123456"}
       - {"username": "test2", "password": "1234562"}

3.3.2 使用 fixtures 功能实现参数化

基本实现原理参考 pytest 框架的参数化实现

import pytest
@pytest.mark.parametrize("test_input,expected",
                         [ ["3+5", 8],
                           ["2+4", 6[,
                           ["6 * 9", 42[,
                         ])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

在上面的用例中,只需要关注参数化的2个变量test_input, expected 也就是在测试用例中传的2个值,可以理解为用例的2个fixture参数 还需要关注测试数据,测试数据结构必须是list,里面是可以迭代的数据,因为有2个变量,所以每组数据是2个值。

在yaml文件中

  • 参数化需要的变量写到 config 的 fixtures 位置
  • 参数化需要的数据写到 parameters

示例 test_params.yml

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

config:
  name: post示例
  fixtures: username, password
  parameters:
    - [test1, '123456']
    - [test2, '123456']

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${username}
      password: ${password}
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$..username, '${username}']
    - eq: [body.json.username, '${username}']

运行yaml文件

pytest test_params.yml

会自动生成2个测试用例

(venv) D:\code\tests>pytest test_params.yml
======================== test session starts ========
platform win32 -- Python 3.8.5, pytest-7.2.0, pluggy-1.0.0
rootdir: D:\code\pytest-yaml-yoyo
plugins: yaml-yoyo-1.0.3
collected 2 items                                                                                          

test_params.yml ..                                   [100%]


=================== 2 passed in 0.77s  ================

3.3.3 parameters 实现参数化

第二种实现方式,可以在fixtures 中传变量,但是测试数据必须是字典类型,从字典的key中动态读取变量名称 test_params_2.yml

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
config:
  name: post示例
  parameters:
    - {"username": "tes1", "password": "123456"}
    - {"username": "tes2", "password": "123456"}

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${username}
      password: ${password}
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$..username, '${username}']
    - eq: [body.json.username, '${username}']

运行yaml文件

pytest test_params.yml

以上2种实现参数化方式效果是一样的

3.4 yaml 中调用内置方法

pytest-yaml-yoyo 插件使用了强大的jinja2 模板引擎,所以我们在yaml文件中可以写很多python内置的语法了。

3.4.1 调用 python 内置方法

举个例子: 我定义了一个变量username的值是test123,但是我引用变量的时候只想取出前面四个字符串,于是可以用到引用变量语法

$(username[:4])

可以直接对变量用python的切片语法

test_fun1.yml

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
config:
  name: 引用内置函数
  variables:
    username: test123

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${username[:4]}
      password: "123456"
  validate:
    - eq: [status_code, 200]
    - eq: [$..username, test]

命令行执行用例

pytest test_fun1.yml

运行结果

POST http://httpbin.org/post HTTP/1.1
Host: httpbin.org
User-Agent: python-requests/2.28.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 42
Content-Type: application/json

{"username": "test", "password": "123456"}

3.4.2 字典对象取值

如果定义一个字典类型的变量,我们在取值的时候也可以根据key取值 如定义变量

  variables:
    username: test123
    body:
      user: yoyo
      email: 123@qq.com

user和email的取值用2种方式,通过点属性或者用字典取值方法[key]

      username: ${body.user}
      email: ${body["user"]}

test_fun2.yml完整示例

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
config:
  name: 引用内置函数
  variables:
    username: test123
    body:
      user: yoyo
      email: 123@qq.com

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${body.user}
      password: "123456"
      email: ${body["user"]}
  validate:
    - eq: [status_code, 200]
    - eq: [$..username, '${body.user}']

3.5 自定义函数功能

一些复杂的逻辑处理,需自己写代码去实现,于是可以自定义函数。

3.5.1 自定义函数

自定义函数的实现,需在conftest.py (pytest 框架内置的插件文件)文件中实现

# conftest.py
# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

from pytest_yaml_yoyo import my_builtins
import uuid
import random


def random_user():
    """生成随机账号 4-16位数字+字符a-z"""
    return str(uuid.uuid4()).replace('-', '')[:random.randint(4, 16)]


# 注册到插件内置模块上
my_builtins.random_user = random_user


if __name__ == '__main__':
    print(random_user())

实现基本原理是自己定义一个函数,然后注册到插件内置模块 my_builtins。这样我们在用例中就能找到该函数方法了

test_fun3.yml 用例中引用内置函数示例

config:
  name: 引用内置函数
  variables:
    username: ${random_user()}
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${username}
      password: "123456"
  validate:
    - eq: [status_code, 200]
    - eq: [$..username, '${username}']

3.5.2 函数传参数

在引用自定义函数的时候,也可以传变量

# conftest.py
# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

from pytest_yaml_yoyo import my_builtins

def func(x):
    return f"hello{x}"


my_builtins.func = func

test_fun4.yml示例

config:
  name: 引用内置函数
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${func("xxx")}
      password: "123456"
  validate:
    - eq: [status_code, 200]

函数还能引用自己在config 中定义的变量

config:
  name: 引用内置函数
  variables:
    var: test123
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${func(var)}
      password: "123456"
  validate:
    - eq: [status_code, 200]

3.5.3 函数返回的结果也能二次取值

如果一个函数返回list类型,我们在用例中也能取出其中的一个值

# conftest.py
# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

from pytest_yaml_yoyo import my_builtins

def func_list():
    return ['test1', 'test2', 'test3']


my_builtins.func_list = func_list

test_fun5.yml示例

config:
  name: 引用内置函数

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${func_list().1}
      password: "123456"
  validate:
    - eq: [status_code, 200]

list类型支持2种取值方式${func_list().1} 或者 ${func_list()[1]}

3.5.4 内置函数的使用

目前暂时提供了3个内置函数,和1个内置对象

  • current_time(f: str = '%Y-%m-%d %H:%M:%S'), 获取当前时间 默认格式为2022-12-16 22:13:00,可以传f参数自定义格式
  • rand_value(target: list) 从返回的 list 结果随机取值, 有小伙伴提到的需求
  • rand_str(len_start=None, len_end=None) 生成随机字符串,默认32位

还提供了一个内置的fake 对象,可以生成随机手机号,随机身份证,姓名等数据

使用方法:${fake.name()}, fake.phone_number(), fake.email() 等,具体查看Faker模块提供的方法https://www.cnblogs.com/yoyoketang/p/14869348.html

current_time() 获取当前时间, 使用示例

获取当前时间:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: ${current_time()}
      password: "123456"
  validate:
    - eq: [status_code, 200]

rand_value(target: list) 从返回的 list 结果随机取值, 有小伙伴提到的需求

提取list值:
-
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      data: ["hello", "world", "hello world"]
  extract:
    res: $.json.data
  validate:
    - eq: [status_code, 200]

随机取一个结果:
-
  request:
    method: GET
    url: http://httpbin.org/get
    params:
      key: ${rand_value(res)}
  validate:
    - eq: [status_code, 200]

rand_str(len_start=None, len_end=None) 生成随机字符串,默认32位

rand_str 使用方法: ${rand_str()} 得到32位字符串 ${rand_str(3)} 得到3位字符串 ${rand_str(3, 10)} 得到3-10位字符串

以上yaml,生成的json数据示例

"json": {
    "password": "07d",
    "username": "c1c91161b4"
  }

3.5.5 fake 对象的使用

内置的 fake 对象 (注意是fake,不是faker, 因为faker 是模块名称,避免冲突) ,可以生成随机手机号,随机身份证,姓名等数据

获取当前时间:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      name: ${fake.name()}
      tel: ${fake.phone_number()}
      email: ${fake.email()}
  validate:
    - eq: [status_code, 200]

生成的测试数据

{'name': '王建平', 'tel': 13056609200, 'email': 'jluo@example.net'}

其它更多方法参考Faker模块提供的方法https://www.cnblogs.com/yoyoketang/p/14869348.html

3.6 钩子功能

3.6.1 response 钩子功能

在发送请求的时候,我们希望在发送请求参数前,带上签名的值,或者返回的内容需要二次处理,解密后返回。 此功能我们可以用 hooks 钩子来实现 hooks 功能在v1.0.4版本上实现

requests 库只支持一个 response 的钩子,即在响应返回时可以捎带执行我们自定义的某些方法。 可以用于打印一些信息,做一些响应检查或想响应对象中添加额外的信息

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
import requests
url = 'https://httpbin.org/get'


def response_status(resopnse, *args, **kwargs):
    print('url', resopnse.url)
    resopnse.status = 'PASS' if resopnse.status_code < 400 else 'FAIL'


res = requests.get(url, hooks={'response': response_status})
print(res.status)

以上是基于requests 库的钩子功能实现的基本方式

3.6.2 yaml 用例中添加response 钩子

在yaml 文件中添加response 钩子功能,跟上面代码方式差不多, 有2种方式

  • 1.写到config 全局配置,每个请求都会带上hooks
  • 2.写到单个请求的request 下,仅单个请求会带上hooks功能

先看单个请求的response 钩子 test_hook1.yml

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
config:
  name: post示例
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]

在 conftest.py 中注册钩子函数

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

def hook_response(response, *args, **kwargs):
    # print(response.text) 原始数据
    print("执行response hook 函数内容....")
    class NewResponse:
        text = '{"code": 0, "data": {"token": "yo yo"}}'  # response.text解密
        history = response.history
        headers = response.headers
        cookies = response.cookies
        status_code = response.status_code
        raw = response.raw
        is_redirect = response.is_redirect
        content = b'{"code": 0, "data": {"token": "yo yo"}}'  # response.text解密
        elapsed = response.elapsed

        @staticmethod
        def json():
            # 拿到原始的response.json() 后解码
            return {"code": 0, "data": {"token": "yo yo"}}

    return NewResponse

my_builtins.hook_response = hook_response

由于上面用例只在第一个请求中使用了hooks功能,所以第二个请求的断言- eq: [$.code, 0] 会失败

3.6.3 钩子方法调用语法

  • 1.层级是在request 下
  • 2.hooks 关键字对应的是一个字典 {"response": []}
  • 3.response 的值可以是单个函数名称,也可以是多个func1, func2,或者是一个list类型[func1, func2]
  • 4.response 的值必须是一个可以调用的函数,此函数需在conftest 中注册绑定到my_builtins 模块
  • 5.调用的函数第一个参数是response, 可以重写response内容(如需要对返回结果解密),也可以不用重写
  request:
    method: POST
    url: http://httpbin.org/post
    hooks:
      response: ['hook_response']

3.6.4 config 全局钩子使用

在config 中配置全局hooks功能,格式如下

config:
  name: post示例
  hooks:
    response: ['hook_response']

test_hook2.yml完整示例

config:
  name: post示例
  hooks:
    response: ['hook_response']
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]

全局配置hooks, 那么该用例下所有的请求都会带上hooks

3.6.5 请求预处理钩子

如果需要对请求参数预处理,我们还新增了一个request 请求钩子,可以获取到发送请求时的request参数

在conftest.py

# 作者-上海悠悠 微信/QQ交流:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/


def func1(req):
    print(f'请求预处理:{req}')
    
    
def func2():
    print(f'请求预处理-----------')
    
    
my_builtins.func1 = func1
my_builtins.func2 = func2

在 yaml 文件中使用示例

config:
  name: post示例
teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      request: ['func1', 'func2']
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"
    hooks:
      response: ['hook_response']
  extract:
      url:  body.url
  validate:
    - eq: [status_code, 200]
    - eq: [headers.Server, gunicorn/19.9.0]
    - eq: [$.code, 0]

在config 中设置全局hooks示例

config:
  name: post示例
  hooks:
    request: ['func1', 'func2']
    response: ['hook_response']

由于request 变量是 pytest的一个内置fixture,此变量保留着,获取请求参数的函数使用req 代替。 利用request hook功能可以实现请求参数的预处理,比如请求 body 签名和加密处理等需求。

3.7 用例分层

当我们测试流程类的接口,需反复去调用同一个接口,就会想到复用API,在代码里面可以写成函数去调用。 那么在yaml 文件中,我们可以把单个API写到一个yaml 文件,测试用例去调用导入API。

我这里只分2层:API 层 和 Test case 用例层

  • API 层: 描述接口request请求,可以带上validate 基本的校验
  • Test case 用例层: 用例层多个步骤按顺序引用API

3.7.1 API 层示例

API 层只做接口的描述,一般放到项目根目录api目录下

api/login.yaml 示例

name: post
request:
    method: POST
    url: http://httpbin.org/post
    json:
        username: ${username}
        password: "123456"
validate:
    - eq: [status_code, 200]

如果有需要用到变量,比如登录用户名在不同用例中会用到不同的账号,那么可以使用变量 ${username} 需注意的是,API 层不支持单独运行,因为它只是用例的一个部分,不能当成用例去执行,用例执行需使用 test_*.yml 命名

3.7.2 TestCase 层

用例层通过api 关键字导入需要的API,导入的路径是相对路径,需根据项目的根目录去导入。 比如我的项目结构是这样的

├─api
   └─ login.yml
├─testcase
   └─ test_login.yml
└─conftest.py
└─pytest.ini

那么不管用例文件test_*.yml在哪个目录,都是以项目根目录去导入API 的yaml文件

config:
    name: login case
    base_url: http://127.0.0.1:8000
    variables:
        username: "test123"
        password: "123456"


teststeps:
-
    name: step login1
    api: api/login.yml
    extract:
        url:  body.url
    validate:
        - eq: [status_code, 200]
        - eq: [ok, true]
-
    name: step login2
    api: api/login.yml

运行用例也是在项目根目录去执行 pytest 运行

pytest testcase

3.7.3 关于变量

API 层可以引用变量,引用变量的值都是从用例目录的variables 加载的变量,目前只支持config 设置用例全局变量

config:
    name: login case
    base_url: http://127.0.0.1:8000
    variables:
        username: "test123"
        password: "123456"

我们可以理解为API是用例的一个步骤,是用例的一部分,导入过去相当于复制request 请求到用例步骤里面。

3.7.4 关于校验

在API 层可以写一些基础的校验,比如校验状态码,我们一般不在API层写业务逻辑校验。 比如登录的用例,期望结果可以是登录成功,也可以是登录失败,那么业务逻辑的校验,应该在用例层去校验

-
    name: step login1
    api: api/login.yml
    extract:
        url:  body.url
    validate:
        - eq: [status_code, 200]
        - eq: [ok, true]

如果API 层和用例层都有validate 校验,最后会合并到一起校验。

3.8 写多个用例

一个yaml 文件中可以写多个用例,yaml 文件相当于py模块,每个用例相当于模块里面定义 pytest 的一个函数, 用例名称最好是test开头,如果不是test开头,也会帮你自动拼接成test开头的

3.8.1 实现原理

在 pytest 用例中,我们可以在一个模块写多个函数式的用例,每个用例test开头,如下

import pytest


def test1():
    """用例1"""
    print("hello 111")


def test2():
    """用例2"""
    print("hello 222")


def test3():
    """用例3"""
    print("hello 333")


if __name__ == '__main__':
    pytest.main(['-s', 'test_sample.py'])

执行后会看到3个用例

collected 3 items

test_sample.py hello 111
.hello 222
.hello 333
.

=============== 3 passed in 0.01s ===========

根据以上 pytest 的基本运行原理,于是我们也可以在yaml文件中写出同等的效果

    test1:
        name: 用例1
        print: hello 11111

    test2:
        name: 用例2
        print: hello 22222

    test3:
        name: 用例3
        print: hello 3333

输入 pytest 运行 yaml 用例文件


(venv) D:\demo>pytest test_case.yml -s
=================================== test session starts ===================================
platform win32 -- Python 3.8.5, pytest-7.2.0, pluggy-1.0.0
collected 3 items                                                                          

test_case.yml hello 11111
.hello 22222
.hello 3333
.

==================================== 3 passed in 0.15s ====================================

可以看出执行效果是完全一样的

3.8.2 重新定义了yaml用例格式

为了框架的可扩展性,config 和 teststeps 都不是必须的了,当然以前的格式还是会兼容

    config:
        name: demo

    teststeps:
    -
      name: GET请求示例
      request:
        method: GET
        url: http://httpbin.org/get
      validate:
        - eq: [status_code, 200]

    test1:
        name: 用例1
        print: hello 11111

    test2:
        name: 用例2
        print: hello 22222

用例部分支持2种格式,可以是一个键值对格式

    test1:
        name: 用例1
        print: hello 11111

也可以是一个list


    test1:
     -
        name: 用例1
        print: hello 11111

如果一个用例有多个步骤需要执行,那么用例应该是一个list,会按顺序去执行

    config:
        name: demo
    
    
    test1:
        name: 用例1
        print: hello 11111
    
    test2:
    -
        name: get
        request:
            method: GET
            url: http://httpbin.org/get
        validate:
          - eq: [status_code, 200]
    
    -
        name: post
        request:
            method: POST
            url: http://httpbin.org/post
            json:
              username: test
              password: "123456"
        validate:
          - eq: [status_code, 200]

3.8.3 支持中文命名

用例的函数名称也可以使用中文命名了,这样更直观

config:
    name: demo


用例演示1:
    name: 用例1
    print: hello 11111

用例是多个步骤2:
-
    name: get
    request:
        method: GET
        url: http://httpbin.org/get
    validate:
      - eq: [status_code, 200]

-
    name: post
    request:
        method: POST
        url: http://httpbin.org/post
        json:
          username: test
          password: "123456"
    validate:
      - eq: [status_code, 200]

原有的用例规则不变,只是teststeps 不是必须的关键字,可以用其它的名称,也可以继续使用teststeps (为了兼容大家的使用习惯)

3.9 文件上传

v1.1.3版本上实现文件上传
本插件集成了 requests_toolbelt 插件处理Content-Type: multipart/form-data 类型文件上传接口。

3.9.1 文件上传multipart/form-data

用fiddler抓包,查看抓到的接口,以下这种接口就是multipart/form-data

  • Content-Type: multipart/form-data
  • body参数是这种格式:

-----------------------------22165374713946
Content-Disposition: form-data; title="localUrl"

yoyoketang.png
-----------------------------22165374713946
Content-Disposition: form-data; name="imgFile"; filename="yoyoketang.png"
Content-Type: image/png

3.9.2 在yaml 文件中示例

在postman 中,可以直接选择一个文件上传,非常方便

我们在yaml中也一样,支持文件类的参数,需单独拿出来放到 files 字段里面。

test_upfile.yml 示例

文件上传:
  name: upload file
  request:
      url: http://127.0.0.1:8000/api/v1/upfile/
      method: POST
      data:
          title: 文件上传
      files:
          file: data/abc.jpg

文件abc.jpg 需放到项目根目录data下

files 里面需要传的具体字段,需根据接口文档定义的参数名称。

当然你把其它字符串字段一起放到files 里面也没问题

文件上传:
  name: upload file
  request:
      url: http://127.0.0.1:8000/api/v1/upfile/
      method: POST
      files:
          title: 文件上传
          file: data/abc.jpg

(本插件也是根据你是否在 request 中传了 files 字段来判断是不是需要上传文件)

3.10 sleep和skip、skipif 功能

环境要求
Python 大于等于3.8版本,(低于python3.8版本不支持)
Pytest 7.2.0 最新版

v1.1.4 发布新增3个关键字

  • 1.sleep 添加用例之间的sleep 等待时间
  • 2.skip 跳过用例功能
  • 3.skipif 条件为真时跳过用例
  • 4.查找用例规则优化(之前仅支持查找test开头.yml后缀的用例,现在优化成可以支持.yaml 和 .yml 后缀用例, yaml用例名称可以test开头也可以test结尾,跟pytest查找用例规则保持一致)

pip 安装插件, 最新版本v1.1.4

pip install pytest-yaml-yoyo  

3.10.1 sleep 功能示例

sleep 功能实现time.sleep() 等待时间,sleep参数可以是int类型和float 类型

get请求:
  name: GET请求示例
  sleep: 5
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

sleep 也可以是一个变量,引用config设置的变量值

config:
  variables:
    x: 2.5
get请求:
  name: GET请求示例
  sleep: ${x}
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

sleep 执行顺序按写的顺序执行,如果放到request 前,那就在请求前先执行,放到request后,在请求后执行

3.10.2 skip 跳过用例

pytest 实现跳过用例有2种方式

@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown():
    ...

或者在用例里面跳过

import pytest
if not pytest.config.getoption("--custom-flag"):
    pytest.skip("--custom-flag is missing, skipping tests", allow_module_level=True)

本插件采用的第二种实现方式,在用例里面添加pytest.skip()

skip 关键字后仅支持一个参数,那就是跳过的原因。使用示例

get请求:
  name: GET请求示例
  skip: 功能缺失,暂时跳过此用例
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

运行用例可以看到

如果用例是多个步骤组成的,也可以在步骤中跳过

teststeps:
-
  name: step1--
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

-
  name: step2--
  skip: 功能缺失,暂时跳过此用例
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

那么会按顺序执行,第一个步骤会执行,第二个步骤因为有skip,就跳过了
如果还有第三个步骤,那么一旦遇到skip ,整个用例就会结束,skip 跳过的是用例,而不是步骤!

3.10.3 skipif 满足条件跳过

skipif 后面参数是一个表达式,当表达式运行结果为真,那么就跳过用例

config:
  variables:
    x: 100

get请求:
  name: get
  skipif: ${x} > 50
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

skipif 还可以在多个用例中使用,当前面接口返回数据a,判断a满足条件就跳过后面用例

case1:
  name: get
  request:
    method: GET
    url: http://httpbin.org/get
  extract:
    xx: $.url
  validate:
    - eq: [status_code, 200]


case2:
  name: get
  skipif: "'org' in '${xx}'"
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

skikif 后面的参数需是字符串表达式,通过eval()函数运行后得到结果,判断是否为真,为真的时候通过当前用例。

4.项目配置/报告/日志

4.1 日志功能

pytest 的日志分2个部分:

  • console 控制台输出的日志
  • log_file 保存到本地文件的日志

本插件默认情况下会记录运行日志保存在项目根目录logs下,以当前时间保存txt文本日志内容。 日志默认保存info级别。 console 控制台默认不输出日志

4.1.1 开启 console 控制台日志

控制台直接运行 pytest 是不会用日志输出的,因为默认仅输出 warning 以上的级别日志 有3种方式启动console日志

方法1:命令行带上--log-cli-level参数,设置日志级别

>pytest --log-cli-level=info

方法2: pytest.ini 配置开启日志,并且设置日志级别

[pytest]

log_cli = true
log_cli_level = info

方法3: pytest -o方式重写(即覆盖ini文件中的log相关的命令行参数)

pytest -o log_cli=true -o log_cli_level=INFO

即可在控制台看到日志

-------------------------------------------- live log call --------------------------------------------
2022-12-08 08:30:34 [INFO]: 执行文件-> test_demo.yml
2022-12-08 08:30:34 [INFO]: base_url-> None
2022-12-08 08:30:34 [INFO]: variables-> {}
2022-12-08 08:30:34 [INFO]: 运行 teststeps
2022-12-08 08:30:34 [INFO]: --------  request info ----------
POST http://httpbin.org/post
{
  "method": "POST",
  "url": "http://httpbin.org/post",
  "json": {
    "username": "test",
    "password": "123456"
  }
}
2022-12-08 08:30:35 [INFO]: ------  response info  200 OK  0.495961s------

4.1.2 自定义 console 控制台日志

日志的格式和时间格式也可以自定义设置

[pytest]

log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s %(filename)s:%(lineno)s [%(levelname)s]: %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S

4.1.3 自定义保存日志文件

本插件默认情况下会记录运行日志保存在项目根目录logs下,以当前时间保存txt文本日志内容。 日志默认保存info级别。 如果你想改变这些默认的行为,自定义日志文件目录和名称,可以在pytest.ini 配置日志文件 (log_file 相关的结果是保存日志文件到本地)

[pytest]

log_cli = true
log_cli_level = info
log_cli_format = %(asctime)s %(filename)s:%(lineno)s [%(levelname)s]: %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S

log_file = ./yoyo.log
log_file_level = debug
log_file_format = %(asctime)s %(filename)s:%(lineno)s [%(levelname)s]: %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S

4.1.4 命令行参数配置

log日志的配置也可以用命令行参数配置(pytest -h可以查看)

 --no-print-logs              disable printing caught logs on failed tests.
 --log-level=LOG_LEVEL         logging level used by the logging module
 --log-format=LOG_FORMAT      log format as used by the logging module.
 --log-date-format=LOG_DATE_FORMAT      log date format as used by the logging module.
 --log-cli-level=LOG_CLI_LEVEL        cli logging level.
 --log-cli-format=LOG_CLI_FORMAT        log format as used by the logging module.
 --log-cli-date-format=LOG_CLI_DATE_FORMAT      log date format as used by the logging module.
 --log-file=LOG_FILE               path to a file when logging will be written to.
 --log-file-level=LOG_FILE_LEVEL      log file logging level.
 --log-file-format=LOG_FILE_FORMAT      log format as used by the logging module.
 --log-file-date-format=LOG_FILE_DATE_FORMAT      log date format as used by the logging module.

还可以使用 pytest -o 方式重写(即覆盖 ini 文件中的 log 相关的命令行参数)

pytest pytest  test_log.py -o log_cli=true -o log_cli_level=INFO

4.2 allure 报告

本插件是基于 pytest 框架开发的,所以 pytest 的插件都能使用 allure 报告功能在 v1.0.8 版本上实现

4.2.1 allure 环境准备

allure 是一个命令行工具,需要去github上下载最新版https://github.com/allure-framework/allure2/releases

allure 命令行工具是需要依赖jdk 环境,环境内容自己去搭建了

4.2.2 生成 allure 报告

在用例所在的目录执行命令, --alluredir 是指定报告生成的目录

pytest --alluredir ./report

打开allure 报告执行命令

>allure serve ./report

4.2.3 查看报告

首页显示

图表查看

用例详情根据yaml文件名称和用例名称展示内容

4.3 全局 base_url

一个完整的url 地址由环境地址和接口地址拼接而成,环境地址是可变的,可以部署到测试环境,uat联调环境等不同的环境。 不管部署到哪个环境,接口的地址是不可变的,通常需要一个全局base_url 地址做到环境可切换。 pip 安装插件

pip install pytest-yaml-yoyo

base_url 全局配置功能在 v1.0.9 版本上实现

4.3.1 环境地址

在接口测试中,通常会把环境 base_url 地址独立出来 比如一个完整的请求http://httpbin.org/get 那么可以分成环境地址http://httpbin.org 和 接口地址 /get

在 yaml 用例中,可以把 base_url 单独拿出来放到 config 下

config:
  base_url: http://httpbin.org

get示例:
  name: get demo
  request:
    method: GET
    url: /get
  validate:
    - eq: [status_code, 200]

post示例:
  name: get demo
  request:
    method: POST
    url: /post
  validate:
    - eq: [status_code, 200]

4.3.2 全局 base_url 配置

从项目的角度讲,测试项目接口的 base_url 都是一样的,所以我们只需全局设置一个就行了,不需要每个yaml 文件中重复去写。 于是可以在pytest.ini 里面配置全局base_url

[pytest]

base_url = http://httpbin.org

那么yaml用例就不需要写 base_url 了,默认会引用pytest.ini的全局配置

yaml 用例1

config:
  name: demo1

get示例:
  name: get demo
  request:
    method: GET
    url: /get
  validate:
    - eq: [status_code, 200]

yaml 用例2

config:
  name: demo2

post示例:
  name: get demo
  request:
    method: POST
    url: /post
  validate:
    - eq: [status_code, 200]

除了可以在pytest.ini 配置base_url 参数,也可以通过命令行参数--base-url去设置

pytest --base-url=http://httpbin.org

4.3.3 复杂情况

当设置了全局base_url 后,有部分用例的环境地址不是同一个的时候,我们可以在yaml文件中config 配置 base_url 去覆盖全局配置环境地址。

config:
  base_url: http://httpbin.org

get示例:
  name: get demo
  request:
    method: GET
    url: /get
  validate:
    - eq: [status_code, 200]

或者请求 url 地址用绝对地址

config:
  name: demo

get示例:
  name: get demo
  request:
    method: GET
    url: http://httpbin.org/get
  validate:
    - eq: [status_code, 200]

4.3.4 使用优先级

环境地址优先级使用如下: 1.全局配置命令行参数--base-url优先级大于 pytest.ini 文件中的 base_url 配置。 2.yaml 文件 config 中的base_url 优先级大于全局配置 3.request 请求的url 如果是绝对地址,那么base_url 无效

总的来说 : url 绝对地址 > config 中的base_url > pytest.ini 文件中的base_url > 命令行参数--base-url

4.4 全局项目配置

当我们在测试环境写好自动化的代码,领导说你把代码部署到联调环境再测一测,这时候去改用例里面的配置是很痛苦的。 所以我们在设计自动化用例的时候,就先要想到多环境的配置与切换。

4.4.1 多环境配置

如果需用到多套环境 test/uat 等,那么应该在用例的根目录(pytest.ini 同级文件)创建一个config.py 文件 pip 安装插件

pip install pytest-yaml-yoyo

多套环境切换功能在 v1.0.10 版本上实现


class Config:
    """多套环境的公共配置"""
    version = "v1.0"


class TestConfig(Config):
    """测试环境"""
    BASE_URL = 'http://192.168.1.1:8000'
    MYSQL_HOST = "192.168.1.1"
    MYSQL_USER = "root"
    MYSQL_PASSWORD = "123456"
    MYSQL_PORT = 3306
    MYSQL_DATABASE = "xxx"   # 连接数据的库名


class UatConfig(Config):
    """联调环境"""
    BASE_URL = 'http://192.168.1.3:8080'
    MYSQL_HOST = "http://192.168.1.3"
    MYSQL_USER = "root"
    MYSQL_PASSWORD = "654321"
    MYSQL_PORT = 3306
    MYSQL_DATABASE = "xxx"  # 连接数据的库名


# 环境关系映射,方便切换多环境配置
env = {
    "test": TestConfig,
    "uat": UatConfig
}

按以上的配置格式,配置不同的环境,最后做一个环境名称和配置的映射关系,必须是 env 命名,格式如下

env = {
    "test": TestConfig,
    "uat": UatConfig
}

那么在执行用例的时候,可以选择执行test 环境还是uat 环境,有 2 种方式可以配置待执行的环境

方法一: 在pytest.ini 中配置

[pytest]


env = test

方法二: 执行 pytest 命令的时候设置

pytest --env test

如果2个地方都有设置,那么优先级是:命令行参数 --env test 大于 pytest.ini 中配置env = test.

4.4.2 测试环境的 BASE_URL

在上一篇中讲到 pytest + yaml 框架 -11.全局 base_url 配置

环境地址优先级使用如下:
1.全局配置命令行参数--base-url优先级大于 pytest.ini 文件中的base_url 配置。
2.yaml 文件 config 中的base_url 优先级大于全局配置
3.request 请求的url 如果是绝对地址,那么base_url 无效
总的来说 : url 绝对地址 > config 中的base_url > 命令行参数--base-url > pytest.ini 文件中的base_url

这里我们新增了一个在config.py 中也可以配置全局的base_url (config.py 中的配置用大写命名 BASE_URL)

如果在 config.py 中配置全局的 BASE_URL ,那么也会生效。优先级会低于命令行和 pytest.ini 的配置

总的来说:url 绝对地址 > config 中的base_url > 命令行参数--base-url > pytest.ini 文件中的 base_url > config.py 的 BASE_URL

4.4.3 多个base_url 切换

有同学提到说,如果一个用例中有多个base_url 需要切换,该如何解决?

我们在配置项中BASE_URL 项是设置默认的全局base_url地址,如果有多个地址,我们还可以用其它的配置参数,比如 BLOG_URLDEMO_URL


class TestConfig(Config):
    """测试环境"""
    BASE_URL = 'http://192.168.1.1:8000'
    BLOG_URL = 'https://www.cnblogs.com'
    DEMO_URL = 'http://httpbin.org'
    MYSQL_HOST = "192.168.1.1"
    MYSQL_USER = "root"
    MYSQL_PASSWORD = "123456"
    MYSQL_PORT = 3306
    MYSQL_DATABASE = "xxx"   # 连接数据的库名

上面的配置中,我们就配置了3个环境地址

    BASE_URL = 'http://192.168.1.1:8000'
    BLOG_URL = 'https://www.cnblogs.com'
    DEMO_URL = 'http://httpbin.org'

在yaml 用例中,如果没有传base_url, 那么会用默认的 BASE_URL = 'http://192.168.1.1:8000'

使用示例

config:
  name: 示例

用例:
-
  name: GET
  request:
    method: GET
    url: /get
  validate:
    - eq: [status_code, 200]

当一个接口中同时用到3个测试地址 BASE_URLBLOG_URLDEMO_URL 时,可以通过 env 变量取值,如: ${env.BLOG_URL}

config:
  name: 示例

用例:

-
  name: GET
  request:
    method: GET
    url: ${env.BLOG_URL}/yoyoketang
  validate:
    - eq: [status_code, 200]

-
  name: GET
  request:
    method: GET
    url: ${env.DEMO_URL}/get
  validate:
    - eq: [status_code, 200]

-
  name: GET
  request:
    method: GET
    url: /get
  validate:
    - eq: [status_code, 200]

4.4.4 其它配置参数

如果配置中需要加其它配置参数

class TestConfig(Config):
    """测试环境"""
    BASE_URL = 'http://192.168.1.1:8000'
    USER = 'test'
    TEL = '100860000'

那么在用例中引用配置参数可以用 ${env.配置参数} 取到配置中的值。

4.5 mysql 数据库配置

如果用例中需要执行mysql 数据库,或者在断言的时候需要查询mysql 数据库。先在config.py 中完成配置

class TestConfig(Config):
    """测试环境"""
    BASE_URL = 'http://192.168.1.1:8000'
    MYSQL_HOST = "192.168.1.1"
    MYSQL_USER = "root"
    MYSQL_PASSWORD = "123456"
    MYSQL_PORT = 3306
    MYSQL_DATABASE = "xxx"   # 连接数据的库名

当完成了MYSQL 相关的五个配置,那么有个内置的函数可以使用

  • query_sql(sql) 查询sql, 查询无结果返回None, 查询只有一个结果返回dict, 查询多个结果返回list of dict
  • execute_sql(sql) 执行sql, 操作新增,修改,删除的sql

4.5.1 断言执行sql

使用示例

config:
  base_url: http://192.168.1.1:8000
  variables:
    username: test
    sql: select * from auth_user where username like 'test';

登录:
    name: step login
    request:
        url: /api/v1/login
        method: POST
        json:
            username: ${username}
            password: "123456"
    extract:
        token: $.token
    validate:
        - eq: [status_code, 200]
        - eq: [ok, true]
        - eq: [$.username, '${query_sql(sql).username}']

以上示例是断言的时候,执行sql,获取数据库的值

- eq: [$.username, '${query_sql(sql).username}']

可以开启日志

[pytest]

log_cli = true
log_cli_level = debug
env = test

查看运行日志

body:
     {"code": 0, "msg": "login success!", "username": "test", "token": "6112772900193da079e9fcc857613f6125
3648fd"}

2022-12-13 10:34:54 [INFO]: extract 提取变量-> {'token': '6112772900193da079e9fcc857613f61253648fd'}
2022-12-13 10:34:54 [DEBUG]: query sql: select * from auth_user where username like 'test';!
2022-12-13 10:34:54 [INFO]: query result: {'id': 2, 'password': 'pbkdf2_sha256$100000$rSQNBkIc2xOm$VGXiUZk
dsIueT/AsoPwlFSEL1vGODsK7eIjK0nawH/M=', 'last_login': None, 'is_superuser': 0, 'username': 'test', 'first_
name': '', 'last_name': '', 'email': '478391@qq.com', 'is_staff': 0, 'is_active': 1, 'date_joined': dateti
me.datetime(2022, 11, 11, 21, 22, 59, 971425)}
2022-12-13 10:34:54 [INFO]: validate 校验内容-> [{'eq': ['status_code', 200]}, {'eq': ['ok', True]}, {'eq'
: ['$.username', 'test']}]
2022-12-13 10:34:54 [INFO]: validate 校验结果-> eq: [200, 200]
2022-12-13 10:34:54 [INFO]: validate 校验结果-> eq: [True, True]
2022-12-13 10:34:54 [INFO]: validate 校验结果-> eq: [test, test]

从返回的body 里面提取username 使用表达式$.username, 得到实际结果"test" '${query_sql(sql).username}' 表达式会先调用query_sql(sql) 函数,引用前面设置的变量sql, 得到结果

{'id': 2, 'password': 'pbkdf2_sha256$100000$rSQNBkIc2xOm$VGXiUZk
dsIueT/AsoPwlFSEL1vGODsK7eIjK0nawH/M=', 'last_login': None, 'is_superuser': 0, 'username': 'test', 'first_
name': '', 'last_name': '', 'email': '478391@qq.com', 'is_staff': 0, 'is_active': 1, 'date_joined': dateti
me.datetime(2022, 11, 11, 21, 22, 59, 971425)}

得到的结果是一个字典,字典对象可以继续取值,于是'${query_sql(sql).username}' 就可以得到期望结果 "test"

4.5.2 用例的参数也可以查询sql

如果用例的参数,需要从sql中取值,我们也可以先定义变量,在用例中引用query_sql(sql) 函数

config:
  variables:
    sql: select * from auth_user where username like 'test';

登录:
    name: step login
    request:
        url: /api/v1/login
        method: POST
        json:
            username: ${query_sql(sql).username}
            password: "123456"
    extract:
        token: $.token
        x: ${query_sql(sql).username}
    validate:
        - eq: [status_code, 200]
        - eq: [ok, true]
        - eq: [$.username, test]

extract 中也可以支持执行sql,得到提取结果

  extract:
        token: $.token
        x: ${query_sql(sql).username}

4.5.3 用例前置和后置执行sql

如果需要在用例的前置和后置中执行sql, 可以用到hook 机制,在请求前和请求后执行函数 参考前面这篇pytest + yaml 框架 -6.hooks 钩子功能实现

4.6 全局代理

在实际的工作中,有些系统的接口我们无法直接访问,需使用代理去访问,那么就需要在整个项目的用例中配置一个全局代理ip
环境要求
Python 大于等于3.8版本,(低于python3.8版本不支持)
Pytest 7.2.0 最新版

pip 安装插件, 最新版本v1.1.6,此功能在v1.1.6版本上实现

pip install pytest-yaml-yoyo  

支持2种方式实现
1.在命令行执行的时候带上 --proxies-ip=代理ip:端口

>pytest test_xxx.yml --proxies-ip=127.0.0.1:8080  

2.可以在pytest.ini 添加全局配置

[pytest]  
  
proxies_ip = 127.0.0.1:8080  

注意配置的ip和端口,前面的 http/https 前缀不需要

4.6.1 使用示例

test_pp.yaml 用例文件中不需要添加额外的参数

config:
  name: post示例

teststeps:
-
  name: post
  request:
    method: POST
    url: http://httpbin.org/post
    json:
      username: test
      password: "123456"

方式1:命令行运行

> pytest test_pp.yml --proxies-ip=127.0.0.1:8080  

方式2:使用 pytest.ini 添加全局配置

[pytest]  
  
proxies_ip = 127.0.0.1:8080  

5.反馈通知

5.1 钉钉机器人通知

当用例执行完成后,希望能给报告反馈,常见的报告反馈有:邮箱/钉钉群/飞书/企业微信 等。 pip 安装插件

pip install pytest-yaml-yoyo

钉钉机器人通知测试结果功能在v1.1.1版本实现

5.1.1 钉钉机器人设置

钉钉机器人的设置请参考官方API文档https://open.dingtalk.com/document/group/custom-robot-access

我们主要得到Webhook地址上面的access_token 值

自定义关键字,默认:测试报告,也可以自定义

加签 的值,可以不勾选,也可以勾选。

总的来说,需要记住3个地方:

  • access_token 复制Webhook地址上面的access_token 值
  • 自定义关键字 默认:测试报告,也可以自定义其他名称,如果这里改了,后面的配置的title值也要改成一样
  • 加签 的值,可以不勾选,也可以勾选。如果勾选了,后面需配置secret 值

5.1.2 config 中配置 DING_TALK 项

在config 中配置 DING_TALK, 只有 access_token 值是必须项, 如果配置了 DING_TALK ,那么就会自动启动发送钉钉机器人通知。 如果不启动钉钉机器人通知测试报告,那么把此项注掉即可。

class Config:
    version = "v1.0"


class TestConfig(Config):
    """测试环境"""
    BASE_URL = 'http://127.0.0.1:8000'
    # 钉钉群机器人通知
    DING_TALK = {
        "access_token": "d2433d2b16cc85*************************************",
    }


class UatConfig(Config):
    """联调环境"""
    BASE_URL = 'http://192.168.1.1:8001'



# 环境关系映射,方便切换多环境配置
env = {
    "test": TestConfig,
    "uat": UatConfig
}

在pytest.ini 中配置

[pytest]


env = test

DING_TALK 相关参数说明

  • access_token: 钉钉群自定义机器人access_token
  • secret: 机器人安全设置页面勾选"加签"时需要传入的密钥
  • param pc_slide: 消息链接打开方式,默认False为浏览器打开,设置为True时为PC端侧边栏打开
  • param fail_notice: 消息发送失败提醒,默认为False不提醒,开发者可以根据返回的消息发送结果自行判断和处理
  • param title: 首屏会话透出的展示内容
  • param text: markdown格式的消息内容
  • param is_at_all: @所有人:True,否则为:False(可选), 默认False
  • param at_mobiles: 被@人的手机号, 手机号可以是一个或者多个,写到list
  • param at_dingtalk_ids: 被@用户的UserId(企业内部机器人可用,可选),可以是一个或者多个,写到list
  • param is_auto_at: 是否自动在text内容末尾添加@手机号,默认自动添加,也可设置为False,然后自行在text内容中自定义@手机号的位置,才有@效果,支持同时@多个手机号(可选)

运行用例后会自动在钉钉群发送通知

5.1.3 加签值配置

如果这里没有勾选 加签 值

那么只需要配置一个 access_token 即可

DING_TALK = {
        "access_token": "d2433d2b16cc85943*********************************",
    }

如果这里勾选 加签 值, 那么需同时配置 access_token 和 secret 值

DING_TALK = {
        "access_token": "d2433d2b16cc85943*********************************",
        "secret": "**************************"
    }

5.1.3 设置@指定的人

奈特指定的人有3个配置可以选择

  • is_at_all @所有人:True,否则为:False(可选)
  • at_mobiles: 被@人的手机号,可以是一个或者多个,写到list
  • at_dingtalk_ids: 被@用户的UserId(企业内部机器人可用,可选),可以是一个或者多个,写到list

使用示例

 DING_TALK = {
        "access_token": "d2433d2b16cc85****************",
        "at_mobiles": ["15000xxxxxxx", "15001xxxxxxx"]
    }

于是就可以看到上图的效果,在内容后面带上@张三的样式

5.1.4 设置 title 和 内容

title 的名称必须与自定义关键字名称保持一致

 DING_TALK = {
        "access_token": "d2433d2b16******************",
        "title": "测试报告",
        "at_mobiles": ["15000xxxxxxx", "15001xxxxxxx"]
    }

报告的 text 内容, 也就是我们看到的

执行结果:

- 运行环境: test
- 运行base_url: http://127.0.0.1:8000
- 持续时间:  0.37 秒

本次运行结果:

- 总用例数: 3
- 通过用例:3
- 失败用例: 0
- 异常用例: 0
- 通过率: 100.0 %

text 的内容,默认是上面的这些,支持markdown 文档格式,如果你需要添加额外的内容,比如加上allure报告地址,那么可以用 text 字段追加内容

 DING_TALK = {
        "access_token": "d2433d2b16cc8594348**************",
        "title": "测试报告",
        "at_mobiles": ["15000xxxxxxx", "15001xxxxxxx"],
        "text": ""text": "- 查看报告:[allure报告地址](https://www.cnblogs.com/yoyoketang/)""
    }

把上面的https://www.cnblogs.com/yoyoketang/ 换成你自己的allure报告地址即可

于是看到以下的效果

总的来说,整个配置都是傻瓜式的,配置非常简单。

6.扩展功能

6.1 swagger.json 自动生成 yaml 接口用例

当项目中有很多个接口的时候,一个个去转成 yaml 文件的用例会很浪费时间,现在大部分格式的接口都有swagger.json 接口文档。 那么我们可以从swagger.json 中解析出接口,自动生成 yaml 格式的用例,就可以大大减少工作量。 此功能在 v1.1.5 版本上实现

环境要求 Python 大于等于3.8版本,(低于python3.8版本不支持) Pytest 7.2.0 最新版

pip 安装插件, 最新版本v1.1.5

pip install pytest-yaml-yoyo

6.1.1 使用示例

目前支持2中方式生成 yaml 用例。 1.如果有本地的 swagger.json 文件,可以放到项目根目录,自己写 a.py 文件 目录结构如下

a.py 文件调用插件中的接口即可

from pytest_yaml_yoyo.swagger_parser import SwaggerToYaml
# 作者 上海-悠悠 微信:283340479

s = SwaggerToYaml('./swagger.json')
s.parse_json()
  1. 如果 有在线的swagger.json 地址,也可以支持在线接口调用

a.py 文件调用插件中的接口即可

from pytest_yaml_yoyo.swagger_parser import SwaggerToYaml
# 作者 上海-悠悠 微信:283340479

s = SwaggerToYaml('http://127.0.0.1:8000/swagger.json')
s.parse_json()

6.1.2 yaml 用例自动生成

执行完成后会在当前项目目录按接口模块生成对应的yaml格式用例

yaml 文件格式示例

生成的用例没有base_url ,只有接口的相对地址,那么可以在当前目录下创建pytest.ini 文件

[pytest]


base_url = http://httpbin.org

相关 功能参考全局 base_url 的设置文档https://www.cnblogs.com/yoyoketang/p/16970491.html

备注说明: 1.并不是所有的swagger.json格式都支持,目前我是按flask项目生成的swagger.json 格式来解析的,其它的未知,思路供学习和参考。 2.目前只实现基础功能,需写上面代码调用此功能,暂不支持命令行操作 3.目前仅仅是抓取接口和请求参数,自动生成用例结构 4.参数部分拿文档的默认值,还需自己去调试,写对应的测试数据 5.后续想法是根据参数的范围,按等价类,边界值去生成对应的用例 6.断言部分功能未实现,目前仅断言状态码为200

7.VIP 付费功能

付费用户可获取最新版本功能,和版本升级服务

7.1 录制功能自动转 yaml 用例

7.1.1 环境准备

录制功能环境没给大家预装,考虑大家 python 版本不太一样,可能有的人装不上。官方文档地址https://docs.mitmproxy.org/stable/ 1.先需要准备mitmproxy 环境,最好是 python3.9 版本, 使用 pip 安装接口

pip install mitmproxy

2.安装完成后在项目本地新建一个recorde.py ,名称随便定义

from pytest_yaml_yoyo.mitm_http import RecoderHTTP
"""
步骤:
1.pip 安装 mitmproxy 环境
> pip install mitmproxy
2.复制这里的代码,新建recorde.py 文件,设置过滤环境如:http://127.0.0.1:8001
3.启动服务
> mitmweb -s ./recorde.py -p 8099
4.电脑开启代理,设置对应端口
5.自动录制抓包转成 yaml 用例
"""


addons = [
    RecoderHTTP(['http://你需要抓的环境地址:8001'])   # 设置过滤环境
]

3.执行命令启动服务, 指定监听 8099 端口

mitmweb -s ./recorde.py -p 8099

启动后我们会看到浏览器打开抓包页面

4.电脑开启代理,设置对应端口

浏览器-设置-系统-打开您计算机的代理设置 开启代理-设置8099打开-并点保存 保存后就可以开始抓包了,电脑上发出去的请求都能抓到,比如浏览器打开你要测试的地址,或者通过python写的接口脚本, postman 上执行的接口都能抓到

5.自动抓包生成 yaml 用例 抓到接口会自动生成 yaml 格式用例,如下

config:
  base_url: http://httpbin.org
post_post:
  request:
    method: POST
    url: /post
    headers:
      Content-Type: application/json
    json:
      user: test
      password: '123456'
  validate:
  - eq: [status_code, 200]
  - eq: [headers."Content-Type", application/json]
  - eq: [$.data, '{"user": "test", "password": "123456"}']
  - eq: [$.origin, 183.193.25.125]
  - eq: [$.url, http://httpbin.org/post]

7.1.2 常用的参数配置

RecoderHTTP 实例化时,可以设置以下几个参数

  • filter_host: 抓取的环境地址,可以是多个
  • ignore_cookies: 是否忽略掉cookies,默认False
  • save_base_url: 是否在 pytest.ini 保存全局base_url环境地址, 默认False
  • save_case_dir: 设置用例保存目录,默认cases

1.默认情况下,只需传一个参数,抓取的环境地址,可以是一个,也可以是多个

addons = [
    RecoderHTTP(['http://httpbin.org'])
]

也可以抓取多个环境地址

addons = [
    RecoderHTTP(['http://httpbin.org', 'https://www.baidu.com'])
]

2.ignore_cookies 是设置是否忽略cookies抓取,默认False

设置为True, 录制的yaml 用例中不会带上cookies

3.save_base_url是否在 pytest.ini 保存全局base_url环境地址, 默认False. 默认情况下,每个yaml 用例中在config 添加base_url 环境地址,兼容抓取多个环境的情况 如果只需抓一个环境的地址,设置全局base_url地址,设置save_base_url=True

addons = [
    RecoderHTTP(['http://httpbin.org'], save_base_url=True)
]

抓取用例时会自动创建 pytest.ini 文件

[pytest]
log_cli = true
base_url = http://httpbin.org

用例抓取完成后,重新打开一个终端窗口,输入 pytest 命令就能执行用例了

7.2 mock 功能

前面已经通过代理实现了抓包自动生成 yaml 用例的功能,通过代理也可以实现 mock 功能。 mock 有2种场景: 1.直接拦截发出去的请求,还未到达服务端,模拟自定义返回结果 2.发出去的请求,服务端有反回,拦截返回的结果,篡改返回内容,模拟自己需要的数据

7.2.1 拦截发出去的请求

先看第一种场景:直接拦截发出去的请求,还未到达服务端,模拟自定义返回结果

mt_mock.py 内容如下

from mitmproxy import http


class MockAPI:

    def request(self, flow: http.HTTPFlow):
        print("------------拦截请求----------------")
        if flow.request.pretty_url == "https://www.cnblogs.com/yoyoketang/":
            # 构造自定义 response
            flow.response = http.Response.make(
                200,   # 返回状态码
                "自定义返回内容: 上海-悠悠",  # 返回content str or bytes
                {"Content-Type": "text/html"}  # 返回 headers
            )

addons = [
    MockAPI()
]

启动服务

>mitmweb -s ./mt_mock.py -p 8099

本机开启代理,设置8099端口。

浏览器访问https://www.cnblogs.com/yoyoketang/ 地址,就会看到模拟的返回结果

7.2.2 拦截返回的结果,篡改返回内容

第二种场景:发出去的请求,服务端有反回,拦截返回的结果,篡改返回内容,模拟自己需要的数据

使用示例:访问http://www.example.com/ 本来返回的是html格式,我改下返回json格式

from mitmproxy import http


class MockAPI:

    def request(self, flow: http.HTTPFlow):
        print("------------拦截请求----------------")
        if flow.request.pretty_url == "https://www.cnblogs.com/yoyoketang/":
            # 构造自定义 response
            flow.response = http.Response.make(
                200,   # 返回状态码
                "自定义返回内容: 上海-悠悠",  # 返回content str or bytes
                {"Content-Type": "text/html"}  # 返回 headers
            )

    def response(self, flow: http.HTTPFlow):
        if flow.request.pretty_url == "http://www.example.com/":
            # 修改返回结果
            print(f'状态码: {flow.response.status_code}')
            flow.response.headers["Content-Type"] = "application/json"
            flow.response.set_text('{"code": 0, "message": "success"}')


addons = [
    MockAPI()
]

7.3 mark 标记功能

pytest可以支持对用例自定义标记, 可以把用例按自己的需要归类标记,比如按用例优秀级,标记一些smoke冒烟测试用例。

7.3.1 pytest 标记基本使用

test_m.py 用例内容

import pytest


@pytest.mark.smoke
def test_login():
    pass


def test_something():
    pass


@pytest.mark.smoke
def test_another():
    pass

执行的时候加-m 参数

(venv) D:\demo\untitled_mark>pytest test_m.py -m smoke
================== test session starts ====================
platform win32 -- Python 3.8.5, pytest-7.3.1, pluggy-1.0.0
rootdir: D:\demo\untitled_mark
configfile: pytest.ini
plugins: allure-pytest-2.13.2, Faker-18.10.1, yaml-yoyo-1.3.0
collected 3 items / 1 deselected / 2 selected                                                                           

test_m.py ..                             [100%]

================ 2 passed, 1 deselected in 0.13s ================

7.3.2 yaml 用例中加 mark 标记

yaml 用例中支持2个地方加标记

  • config 中使用mark, 作用是当前yaml 文件中的全部用例打上标记
  • case 用例中加mark,只针对单个用例打上标记

需注意的是一个用例可以打多个标记,mark 关键字可以是一个字符串,如果是多个标记,可以用mark: name1, name2mark: [name1, name2] 两种格式 test_m.yml

config:
  name: yy
  mark: www


test_a1:
  name: a11
  print: "xx111"

test_a2:
  name: a22
  print: "xx22"

config 中加标记,对test_a1 和 test_a2 都会打上标记

test_n.yml

config:
  name: yy2

test_a3:
  mark: [www, aaa]
  name: a333
  print: "xx333"

test_a4:
-
  name: a444
  mark: aaa
  print: "xx444"

1.执行标记为 www 的用例

>pytest -m www -s

运行结果

collected 4 items / 1 deselected / 3 selected                                                                           

test_m.yml xx111
.xx22
.
test_n.yml xx333
.

================ 3 passed, 1 deselected in 0.31s  ================

2.执行标记为 aaa 的用例

>pytest -m aaa -s

运行结果

collected 4 items / 2 deselected / 2 selected                                                                           

test_n.yml xx333
.xx444
.

================= 2 passed, 2 deselected in 0.31s =================

3.执行标记了www并且也标记aaa的用例

>pytest -m "www and aaa" -s

4.执行没有标记www的用例

>pytest -m "not www" -s

5.执行标记了www或aaa的用例

>pytest -m "www or aaa" -s

7.4 mark 标记对用例运行时长断言

pytest 执行用例的时候,我们希望对用例的运行时间断言,当用例执行时长大于预期标记此用例失败。 @pytest.mark.runtime(1) 运行时长单位是秒 此插件已打包上传到pypi https://pypi.org/project/pytest-runtime-yoyo/

环境准备

pip install pytest-yaml-yoyo

此功能在v1.3.1 版本上实现

7.4.1 代码中实现

基本示例 test_demo.py

import pytest
import time


def test_a1():
    time.sleep(2)


@pytest.mark.runtime(1)
def test_a2():
    time.sleep(2)

运行结果

    ======================== short test summary info =====================
    FAILED test_demo.py::test_a2
    ======================== 1 failed, 1 passed in 4.18s ===============

7.4.2 在 yaml 用例中实现

在yaml 中添加 runtime

config:
  name: yy


test_a1:
  name: a11
  mark: runtime(1)
  sleep: 2
  print: "xx111"

test_a2:
  name: a22
  sleep: 2
  print: "xx22"

也可以在config 中添加runtime 对整个yaml 中的用例标记

config:
  name: yy
  mark: runtime(1)


test_a1:
  name: a11
  sleep: 2
  print: "xx111"

test_a2:
  name: a22
  sleep: 2
  print: "xx22"

如果config 中和用例中都有runtime ,那么用例的runtime优先级>config 中的runtime

7.4.3 全局用例配置

对全部用例设置 runtime 标记,可以在 pytest.ini 中设置全局配置

[pytest]

runtime = 3

也可以在执行 pytest 命令的时候带上命令行参数--runtime

pytest --runtime=3

优先级是: 命令行参数 > pytest.ini 配置 全局配置只针对yaml 中config,测试用例没标记 runtime 的用例生效。 如果yaml 中config,测试用例有标记 runtime,那么优先级是大于全局配置的。

7.5 export 提升全局变量,支持跨yaml文件用例传参

export 导出功能, config 和 case 中都能添加。 简单来说,就是test_a.yml 执行完成后,提取了变量x, 在后面的test_b.yml 和 test_c.yml 中可以直接引用变量x了。

7.5.1 extract 提取变量

在单个测试yaml 用例文件中,可以支持写多个用例,并且extract 提取的变量,在整个yaml文件中都可以直接引用。

test_ext3.yml

# 作者-上海悠悠 wx:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

config:
  name: 提取变量
  variables:
    name: msg

test_ext1:
  name: 提取email
  print: ${name}
  extract:
    email: 123@qq.com


test_ext2:
  name: 引用extract 变量
  print: ${email}

test_ext3:
-
  name: 步骤1
  print: ${email}

-
  name: 步骤2
  print: ${email}

使用pytest test_ext3.yml 命令运行用例

(venv) D:\demo>pytest test_ext3.yml
=========================== test session starts ==================================================
platform win32 -- Python 3.8.5, pytest-7.3.0, pluggy-1.0.0
rootdir: D:\demo\untitled6_demo_test
configfile: pytest.ini
plugins: allure-pytest-2.13.1, Faker-18.4.0, yaml-yoyo-1.2.3
collecting ... 
----------------- live log collection ---------------------------
2023-05-14 10:15:22 [INFO]: --------[{'name': '提取email', 'print': '${name}', 'extract': {'email': '123@qq.com'}}]
2023-05-14 10:15:22 [INFO]: --------[{'name': '引用extract 变量', 'print': '${email}'}]
2023-05-14 10:15:22 [INFO]: --------[{'name': '步骤1', 'print': '${email}'}, {'name': '步骤2', 'print': '${email}'}]
collected 3 items                                                                                                        

test_extract/test_ext3.yml::test_ext1
------------------- live log call ------------------------
2023-05-14 10:15:22 [INFO]: 执行文件-> test_ext3.yml
2023-05-14 10:15:22 [INFO]: base_url-> http://124.70.221.221:8201
2023-05-14 10:15:22 [INFO]: config variables-> {'name': 'msg'}
2023-05-14 10:15:22 [INFO]: 运行用例-> test_ext1
2023-05-14 10:15:22 [INFO]: 取值表达式 name
2023-05-14 10:15:22 [INFO]: 取值结果:msg, <class 'str'>
2023-05-14 10:15:22 [INFO]: extract  提取变量-> {'email': '123@qq.com'}
2023-05-14 10:15:22 [INFO]: validate 校验内容-> []
2023-05-14 10:15:22 [INFO]: export 导出全局变量:{}
PASSED                                                                                                             [ 33%]
test_extract/test_ext3.yml::test_ext2
------------------- live log call -------------------
2023-05-14 10:15:22 [INFO]: 执行文件-> test_ext3.yml
2023-05-14 10:15:22 [INFO]: base_url-> http://124.70.221.221:8201
2023-05-14 10:15:22 [INFO]: config variables-> {'name': 'msg', 'email': '123@qq.com'}
2023-05-14 10:15:22 [INFO]: 运行用例-> test_ext2
2023-05-14 10:15:22 [INFO]: 取值表达式 email
2023-05-14 10:15:22 [INFO]: 取值结果:123@qq.com, <class 'str'>
2023-05-14 10:15:22 [INFO]: validate 校验内容-> []
2023-05-14 10:15:22 [INFO]: export 导出全局变量:{}
PASSED                                                                   [ 66%]
test_extract/test_ext3.yml::test_ext3
------------- live log call ------------
2023-05-14 10:15:22 [INFO]: 执行文件-> test_ext3.yml
2023-05-14 10:15:22 [INFO]: base_url-> http://124.70.221.221:8201
2023-05-14 10:15:22 [INFO]: config variables-> {'name': 'msg', 'email': '123@qq.com'}
2023-05-14 10:15:22 [INFO]: 运行用例-> test_ext3
2023-05-14 10:15:22 [INFO]: 取值表达式 email
2023-05-14 10:15:22 [INFO]: 取值结果:123@qq.com, <class 'str'>
2023-05-14 10:15:22 [INFO]: validate 校验内容-> []
2023-05-14 10:15:22 [INFO]: 取值表达式 email
2023-05-14 10:15:22 [INFO]: 取值结果:123@qq.com, <class 'str'>
2023-05-14 10:15:22 [INFO]: validate 校验内容-> []
2023-05-14 10:15:22 [INFO]: export 导出全局变量:{}
PASSED                                                                     [100%]

=============== 3 passed in 0.38s ==

7.5.2 export 提升全局变量

test_ext3.yml 用例中提取的email 变量作用范围仅在test_ext3.yml 中使用有效,无法跨yaml 文件引用。 如果想后面的用例,继续使用提取的email 变量, 需使用export 关键字,提升变量的级别为session会话级别,也就是真正的全局变量。 export 关键字可以写到config 也可以写到用例中,格式必须是list 类型。 test_extract/test_ext3.yml

config:
  name: 提取变量
  variables:
    name: msg
  export:
    - email

test_ext1:
  name: 提取email
  print: ${name}
  extract:
    email: 123@qq.com

在接下来的test_ext4.yml 和其它用例可以直接引用 ${export} test_extract/test_ext4.yml

# 作者-上海悠悠 wx:283340479
# blog地址 https://www.cnblogs.com/yoyoketang
config:
  name: 提取变量

test_ext5:
-
  name: 步骤1
  print: ${email}

-
  name: 步骤2
  print: ${email}

test_ext6:
  name: 6666
  print: ${email}

前提条件是 test_ext3.yml 用例要先执行,pytest 在执行的时候按用例名称顺序执行, 可以放到test_extract 同一个文件夹下一起执行

pytest test_extract

执行后部分log日志

test_extract/test_ext4.yml::test_ext6
----------------------------------------------------- live log call -----------------------------------------------------
2023-05-14 10:22:07 [INFO]: 执行文件-> test_ext4.yml
2023-05-14 10:22:07 [INFO]: base_url-> http://124.70.221.221:8201
2023-05-14 10:22:07 [INFO]: config variables-> {}
2023-05-14 10:22:07 [INFO]: 运行用例-> test_ext6
2023-05-14 10:22:07 [INFO]: 取值表达式 email
2023-05-14 10:22:07 [INFO]: 取值结果:123@qq.com, <class 'str'>
2023-05-14 10:22:07 [INFO]: validate 校验内容-> []
2023-05-14 10:22:07 [INFO]: export 导出全局变量:{'email': '123@qq.com'}
PASSED

export 关键字也可以在用例中使用,跟config 中使用效果引用,如果2个地方都有,会自动合并

test_extract/test_ext3.yml

config:
  name: 提取变量
  variables:
    name: msg

test_ext1:
  name: 提取email
  print: ${name}
  extract:
    email: 123@qq.com
  export:
    - email

7.5.3 变量优先级

在整个用例中有 config 配置文件中设置的env 环境变量,export 导出的session 变量,yaml 文件中的config 模块变量,用例中的variables 局部变量。 整体优先级是:用例中的variables 局部变量 > extract 提取变量 > yaml 文件中的config 模块变量 > export 导出的session 变量 > 配置文件中设置的env 环境变量 test_extract/test_ext5.yml

config:
  name: 提取变量

test_ext7:
  name: 6666
  variables:
    email: yoyo@qq.com
  print: ${email}

test_ext8:
  name: 6666
  print: ${email}

pytest test_extract执行用例

test_extract/test_ext5.yml::test_ext7
----------------------------------------------------- live log call -----------------------------------------------------
2023-05-14 10:34:16 [INFO]: 执行文件-> test_ext5.yml
2023-05-14 10:34:16 [INFO]: base_url-> http://124.70.221.221:8201
2023-05-14 10:34:16 [INFO]: config variables-> {}
2023-05-14 10:34:16 [INFO]: 运行用例-> test_ext7
2023-05-14 10:34:16 [INFO]: 取值表达式 email
2023-05-14 10:34:16 [INFO]: 取值结果:yoyo@qq.com, <class 'str'>
2023-05-14 10:34:16 [INFO]: validate 校验内容-> []
2023-05-14 10:34:16 [INFO]: export 导出全局变量:{'email': '123@qq.com'}
PASSED                                                                                                             [ 54%]
test_extract/test_ext5.yml::test_ext8
----------------------------------------------------- live log call -----------------------------------------------------
2023-05-14 10:34:16 [INFO]: 执行文件-> test_ext5.yml
2023-05-14 10:34:16 [INFO]: base_url-> http://124.70.221.221:8201
2023-05-14 10:34:16 [INFO]: config variables-> {}
2023-05-14 10:34:16 [INFO]: 运行用例-> test_ext8
2023-05-14 10:34:16 [INFO]: 取值表达式 email
2023-05-14 10:34:16 [INFO]: 取值结果:123@qq.com, <class 'str'>
2023-05-14 10:34:16 [INFO]: validate 校验内容-> []
2023-05-14 10:34:16 [INFO]: export 导出全局变量:{'email': '123@qq.com'}
PASSED                      

可以看到 test_ext7 用例引用局部变量值yoyo@qq.com, test_ext8 引用export 导出的 session 会话变量值123@qq.com

7.6 支持飞书机器人通知

7.6.1 自定义飞书机器人

飞书机器人配置详细文档参考:https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN

第一步:邀请自定义机器人入群

进入你的目标群组,打开会话设置,找到群机器人,并点击添加机器人,选择自定义机器人加入群聊。

第二步: 设置机器人名称和描述 为你的机器人输入一个合适的名字和描述,也可以为机器人设置一个合适的头像,然后点击下一步。

第三步:复制 webhook 地址

webhook地址复制出来:https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxxxxxxxxxxx hook/ 后面的一串 xxxxxxxxxxxxxxxxx 就是我们要的 token

7.6.2 config 配置 FEI_SHU

在项目根目录(pytest.ini 同级文件)创建一个config.py 文件

# 作者-上海悠悠 wx:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

class Config:
    """多套环境的公共配置"""
    version = "v1.0"
    # 飞书机器人通知
    FEI_SHU = {
        "token": "*****************",  # 必须
        # "text": "- 查看报告:[allure报告地址](https://www.cnblogs.com/yoyoketang/)"  # 非必须
    }


class TestConfig(Config):
    """测试环境"""
    # ....


class UatConfig(Config):
    """联调环境"""
    # ....


# 环境关系映射,方便切换多环境配置
env = {
    "test": TestConfig,
    "uat": UatConfig
}

在 pytest.ini 中配置运行环境

[pytest]


env = test

7.6.3 运行用例生成报告

通过pytest 命令行运行用例

pytest

运行完成后,会根据你配置的token内容,触发飞书通知

    # 作者-上海悠悠 wx:283340479
    # blog地址 https://www.cnblogs.com/yoyoketang/
    # 飞书机器人通知
    FEI_SHU = {
        "token": "*****************",  # 必须
        # "text": "- 查看报告:[allure报告地址](https://www.cnblogs.com/yoyoketang/)"  # 非必须
    }

用例成功,背景显示绿色

用例失败,背景显示红色

7.7 企业微信机器人通知

7.7.1 获取企业微信机器人token

企业机器人相关接口可以看官方文档https://developer.work.weixin.qq.com/document/path/91770 创建群聊机器人

获取到webhook访问地址https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=********************* key= 后面的这串就是我们要的token

7.7.2 配置企业微信机器人通知

在运行环境配置中加一个WE_COM的配置即可

    # 配置企业微信群通知
    WE_COM = {
        "token": "******复制前面得到的token*********",
    }

配置 WE_COM

class Config:
    """多套环境的公共配置"""
    version = "v1.0"

    # 配置企业微信群通知
    WE_COM = {
        "token": "******复制前面得到的token*********",
    }


class TestConfig(Config):
    """测试环境"""
    BASE_URL = 'http://192.168.1.1:8000'


class UatConfig(Config):
    """联调环境"""
    BASE_URL = 'http://192.168.1.3:8080'



# 环境关系映射,方便切换多环境配置
env = {
    "test": TestConfig,
    "uat": UatConfig
}

在 pytest.ini 中配置运行环境

[pytest]


env = test

执行用例就会自动触发企业微信机器人

7.8 模板过滤器语法与自定义过滤器使用

针对有同学提到上个接口返回一个id值,下个接口引用变量的时候需要根据这个值做一些运算,比如在引用的结果加1. 本篇介绍下模板过滤器的相关使用.

7.8.1 jinja2 模板过滤器语法

什么是 jinja2 模板过滤器?

通过在 Jinja2 模板中嵌入变量,可以输出变量的值。但是,在很多情况下,不仅仅只是需要输出变量的值,还需要修改变量的显示,对变量进行格式化、运算等。 为了方便对变量进行处理,Jinja2 提供了过滤器,Jinja2 过滤器是一个函数,该函数对输入变量进行变换处理后,返回一个值作为 Jinja2 模板的输出。

在 yaml 用例文件中引用变量,使用 Jinja2 中过滤器有如下用法:

示例1:过滤器不带任何参数

${ var | filter }

例如:过滤器 upper 将输入变量 var 转换为大写, ${"hello" | upper } 的输出为 "HELLO"。

示例2:过滤器带参数的情况

${ var | filter(arg) }

例如:过滤器 replace (source, target) 将输入变量 var 中的字符串 source 替换为字符串 target, ${"hello world" | replace ("hello", "yoyo") } 的输出为 "hello yoyo"。

示例3:过滤器可以组合使用

${ var | filterA | filterB }

过滤器可以组合使用,${var | filterA | filterB} 的处理过程如下:

  • 输入变量 var 传递给第一个过滤器 fiterA;
  • 将过滤器 filterA 的输出作为输入,传递给第二个过滤器 filterB;
  • 将过滤器 filterB 的输出作为模板的输出。

例如:${"abc" | upper | reverse } 的输出为 "CBA"。

7.8.2 常用的过滤器

过滤器名称 语法使用 实现功能
capitalize ${ 'yoyo' | capitalize } 首字母转大写: Yoyo
title ${ 'hello world' | title } 单词首字母大写 Hello World
lower ${ 'HELLO' | lower } 转小写: hello
upper ${ 'hello' | upper } 转大写: HELLO
revsere ${ 'abc' | reverse } 反转: cba
format ${ 'my name %s, %d years.' | format('yoyo', 20) } format 格式化字符:my name yoyo, 20 years.
first ${ ['hello', 'world', 'yoyo'] | first} 取出第一个:hello
last ${ ['hello', 'world', 'yoyo'] | last} 取出最后一个:yoyo
length ${ ['hello', 'world', 'yoyo'] | length} 获取列表 list 长度: 3
sum ${ [1, 2, 3] |sum } list 求和: 6
sort ${ [1, 3, 2] | sort } list 排序:123
sort { [1, 3, 2] | sort(reverse = True) } list 倒序:321
join ['hello', 'world', 'yoyo'] | join('_') 字符拼接:hello_world_yoyo
default ${ gender | default('male') } 如果 gender 变量未定义使用默认值:male
add ${20 | add(1)} (这个是我添加的一个add 方法)变量值加1: 21

上面的过滤器方法除了add是我自定义的一个内置方法,其它都是jinja2模板引擎自带的过滤器方法。

7.8.3 使用示例

在yaml 用例中使用过滤器语法 test_a.yml

# 作者-上海悠悠 wx:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/
config:
  name: xx
  variables:
    age: 20
    x: 22
    y: "hell0"

testx1:
  name: "xx"
  print: '${age | add(3)}'

testx2:
  name: "xx"
  print: '${y | length}'

使用pytest 运行用例

pytest test_a.yml

运行结果:

test_a.yml::testx1
------------------------- live log call ------------------------
2023-05-23 10:16:48 [INFO]: 执行文件-> test_a.yml
2023-05-23 10:16:48 [INFO]: base_url->
2023-05-23 10:16:48 [INFO]: config variables-> {'age': 20, 'x': 22, 'y': 'hell0'}
2023-05-23 10:16:48 [INFO]: 运行用例-> testx1
2023-05-23 10:16:48 [INFO]: 取值表达式 age | add(3)
2023-05-23 10:16:48 [INFO]: 取值结果:23, <class 'int'>
2023-05-23 10:16:48 [INFO]: validate 校验内容-> []
2023-05-23 10:16:48 [INFO]: export 导出全局变量:{}
PASSED                                                              [ 50%]
test_a.yml::testx2
---------------------- live log call ------------------------------
2023-05-23 10:16:48 [INFO]: 执行文件-> test_a.yml
2023-05-23 10:16:48 [INFO]: base_url->
2023-05-23 10:16:48 [INFO]: config variables-> {'age': 20, 'x': 22, 'y': 'hell0'}
2023-05-23 10:16:48 [INFO]: 运行用例-> testx2
2023-05-23 10:16:48 [INFO]: 取值表达式 y | length
2023-05-23 10:16:48 [INFO]: 取值结果: 5, <class 'str'>
2023-05-23 10:16:48 [INFO]: validate 校验内容-> []
2023-05-23 10:16:48 [INFO]: export 导出全局变量:{}
PASSED                                                                [100%]

7.8.4 自定义过滤器

自定义过滤器语法跟自定义函数有点类似,也是在项目根路径下conftest.py 文件上注册过滤器

conftest.py

from pytest_yaml_yoyo.render_template_obj import env_filter
# 作者-上海悠悠 wx:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/


def fun_a(value):
    """不带参数"""
    return f"{value} 不带参数: {value}"


def fun_b(value, x):
    """带一个参数x"""
    return f"{value} 带参数x: {x}"

# 注册过滤器
env_filter.filters['fun_a'] = fun_a
env_filter.filters['fun_b'] = fun_b

yaml 中用例使用 test_b.yml

# 作者-上海悠悠 wx:283340479
# blog地址 https://www.cnblogs.com/yoyoketang/

config:
  name: 自定义过滤器的使用
  variables:
    name: yoyo

testa1:
  name: "不带参数"
  print: '${name | fun_a}'

testa2:
  name: "带参数"
  print: '${name | fun_b("bb")}'

运行结果

test_b.yml::testa1
------------------- live log call -----------------------------
2023-05-23 10:38:48 [INFO]: 执行文件-> test_b.yml
2023-05-23 10:38:48 [INFO]: base_url->
2023-05-23 10:38:48 [INFO]: config variables-> {'name': 'yoyo'}
2023-05-23 10:38:48 [INFO]: 运行用例-> testa1
2023-05-23 10:38:48 [INFO]: 取值表达式 name | fun_a
2023-05-23 10:38:48 [INFO]: 取值结果: yoyo 不带参数: yoyo, <class 'str'>
2023-05-23 10:38:48 [INFO]: validate 校验内容-> []
2023-05-23 10:38:48 [INFO]: export 导出全局变量:{}
PASSED                                             [ 75%]
test_b.yml::testa2
------------------------- live log call -------------------
2023-05-23 10:38:48 [INFO]: 执行文件-> test_b.yml
2023-05-23 10:38:48 [INFO]: base_url->
2023-05-23 10:38:48 [INFO]: config variables-> {'name': 'yoyo'}
2023-05-23 10:38:48 [INFO]: 运行用例-> testa2
2023-05-23 10:38:48 [INFO]: 取值表达式 name | fun_b("bb")
2023-05-23 10:38:48 [INFO]: 取值结果: yoyo 带参数x: bb, <class 'str'>
2023-05-23 10:38:48 [INFO]: validate 校验内容-> []
2023-05-23 10:38:48 [INFO]: export 导出全局变量:{}
PASSED                                            [100%]

7.9 支持自定义函数提取返回结果

在 yaml 用例中提取返回结果,可以支持以下三种表达式

  • jmespath 取值语法: body.keyname.keyname
  • jsonpath 语法: $..keyname
  • re 正则语法

以上三种表达式可以满足 99% 的测试场景需求了,但是有些特殊的需求通过表达式无法取到,为了满足另外1%的需求,可以自定义函数取值。 此功能在v1.3.6版本实现

7.9.1 场景描述

有个小伙伴给我提了个需求:如果返回的结果中有某个值就断言,没有就不断言

示例:如下返回结果,当data中name的值为"yoyo"的时候,断言它的邮箱值"283340479@qq.com",如果结果中没有name的值为"yoyo"就不断言

res = {
    "code": 0,
    "msg": "成功success!",
    "data": [
       {
           "age": 20,
           "create_time": "2019-09-15", 
           "id": 1, 
           "mail": "283340479@qq.com", 
           "name": "yoyo", 
           "sex": "M"
       },
       {
           "age": 21, 
           "create_time": "2019-09-16", 
           "id": 2,
           "mail": "123445@qq.com", 
           "name": "yoyo111", 
           "sex": "M"
       }
    ]
}

7.9.2 代码示例

先自定义函数取值,传一个 response (接口返回对象)

conftest.py 内容如下:

from pytest_yaml_yoyo import  my_builtins


def fun_x(response):

    res = response.json()
    for item in res['data']:
        if item.get("name") == "yoyo":
            return item.get("mail") == "283340479@qq.com"
    return True


my_builtins.fun_x = fun_x

yaml 用例中内容, 校验地方可以引用函数${fun_x(response)}, response 参数是接口返回对象。 test_rep.yml

test_rep:
  name: z
  request:
    url: /api/test/demo
    method: get

  validate:
    - eq: ["hello", "hello"]
    - eq: ["${fun_x(response)}", true]

执行用例

pytest test_rep.yml

运行结果:

2023-06-26 22:45:08 [INFO]: 取值表达式: fun_x(response), 取值结果:True <class 'bool'>
2023-06-26 22:45:08 [INFO]: validate 校验内容-> [{'eq': ['hello', 'hello']}, {'eq': [True, True]}]
2023-06-26 22:45:08 [INFO]: validate 校验结果-> eq: [hello, hello]
2023-06-26 22:45:08 [INFO]: validate 校验结果-> eq: [True, True]
2023-06-26 22:45:08 [INFO]: export 导出全局变量:{}

7.10 parameters参数化支持笛卡尔积

v1.3.8 版本对 parameters 参数化格式重新做了定义,支持笛卡尔积了。当然以前旧版本的格式还是继续兼容。

7.10.1 parameters 参数化

新版本对 parameters 参数化重新做了定义,简化了步骤,更加清晰简洁.

1.只有一个变量需要参数化的情况

test_p1.yml

config:
  parameters:
    x: ["a", "b", "c"]


test_a:
  print: "输出-${x}"

运行结果

test_p1.yml 输出-a
.输出-b
.输出-c
.

2.有2个变量需要参数化的情况,变量中间用逗号隔开(或者-) test_p2.yml

config:
  parameters:
    x,y: [["a", "b"], ['c', 'd']]

test_b:
  print: "输出-${x}-${y}"

也可以实现横线隔开

config:
  parameters:
    x-y: [["a", "b"], ['c', 'd']]

运行结果

test_p2.yml 输出-a-b
.输出-c-d
.

7.10.2 笛卡尔积

对2个变量同时参数化,生成笛卡尔积的情况 x 变量只有一个值,可以写成x: "a", 也可以写成x: ["a"] test_p3.yml

config:
  parameters:
    x: "a"
    y: ["hello", "world"]


test_r:
  name: 11
  print: "组合-${x}-${y}"

x 变量也可以有多个值 test_p4.yml

config:
  parameters:
    x: ["a", 'b']
    y: ["hello", "world"]


test_r:
  name: 11
  print: "组合-${x}-${y}"

运行结果

test_p4.yml 组合-a-hello
.组合-a-world
.组合-b-hello
.组合-b-world
.

7.10.3 模块级别和用例级别参数化

支持模块级别和用例级别参数化

  • config 中 parameters 参数化,作用域是整个模块级别
  • 用例中 parameters 参数化,作用域只针对单个用例

用例级别参数化 test_p5.yml

config:
   name: demo

test_1:
  name: 用例1
  parameters:
    user: [hello, world]
  print: "case-${user}"

运行结果

test_p5.yml case-hello
.case-world
.

config 中 parameters 参数化,作用域是整个模块级别, test_p6.yml 文件示例

config:
  parameters:
    x: ["a", 'b']
    y: ["hello", "world"]


test_a:
  print: "组合-${x}-${y}"

test_b:
  print: "组合-${x}-${y}"

运行结果

test_p6.yml 组合-a-hello
.组合-a-hello
.组合-a-world
.组合-a-world
.组合-b-hello
.组合-b-hello
.组合-b-world
.组合-b-world
.

7.11 仅收集用例失败错误信息和log日志

有小伙伴提到能不能只收集用例失败的情况下日志和错误信息,每个用例都收集日志内容太多了,没法直观的看错误的用例信息。 v1.3.9 版本实现此功能,在原有的功能上新增一个log日志文件,仅收集用例错误的日志。

7.11.1 log 日志收集

用例执行完会默认生成2个日志文件

  • 全量日志,每个用例的日志都会收集
  • 仅失败的用例日志

使用示例

config:
   name: demo

test_1:
  name: 用例1
  print: "111111"

test_2:
  name: 用例2
  print: "2222"
  validate:
    - eq: [200, 201]

用例执行后根据当前时间生成一个20230707_201045_error.log 日志文件

************************* a1/test_a2.yml::test_2 *************************
测试结果 outcome:failed   
用例耗时 duration:0.003574687999999826
异常 exception:<ExceptionInfo AssertionError("200-><class 'int'> == 201-><class 'int'>\nassert 200 == 201") tblen=4>
exception详细日志:request = <FixtureRequest for <Function test_2>>
requests_session = <pytest_yaml_yoyo.http_session.HttpSession object at 0x0000022D4EF43D00>

>   ???

test_a2.py:2: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
venv\lib\site-packages\pytest_yaml_yoyo\runner.py:222: in execute_yaml_case
    self.validate_response(response, validate_value)
venv\lib\site-packages\pytest_yaml_yoyo\runner.py:559: in validate_response
    validate.equals(actual_value, expect_value)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

check_value = 200, expect_value = 201

    def equals(check_value, expect_value):
        check_value = None if check_value == 'None' else check_value
        expect_value = None if expect_value == 'None' else expect_value
>       assert check_value == expect_value, f'{check_value}->{type(check_value)} == {expect_value}->{type(expect_value)}'
E       AssertionError: 200-><class 'int'> == 201-><class 'int'>
E       assert 200 == 201

venv\lib\site-packages\pytest_yaml_yoyo\validate.py:10: AssertionError
Captured stdout call2222
Captured log call[32mINFO    [0m pytest_yaml_yoyo.log:runner.py:128 执行文件-> test_a2.yml
[32mINFO    [0m pytest_yaml_yoyo.log:runner.py:129 base_url-> http://124.70.221.221:8201
[32mINFO    [0m pytest_yaml_yoyo.log:runner.py:130 config variables-> {}
[32mINFO    [0m pytest_yaml_yoyo.log:runner.py:132 运行用例-> test_2
[32mINFO    [0m pytest_yaml_yoyo.log:runner.py:221 validate 校验内容-> [{'eq': [200, 201]}]
[32mINFO    [0m pytest_yaml_yoyo.log:runner.py:557 validate 校验结果-> eq: [200, 201]

根据用例节点a1/test_a2.yml::test_2 详细记录用例报错的内容和用例运行的日志.

7.12 allure报告自定义内容

v1.4.0 版本支持allure报告自定义内容

7.12.1 用例添加 allure 描述

用例中可以通过dynamic 添加以下内容

  • allure.dynamic.feature
  • allure.dynamic.link
  • allure.dynamic.issue
  • allure.dynamic.testcase
  • allure.dynamic.story
  • allure.dynamic.title
  • allure.dynamic.description

在test_a.yml 用例中示例

config:
  name: 用例描述


test_a1:
  name: 用例a1
  allure:
    feature: demo模块
    story: 用例场景
    title: 用例详细描述a1
    severity: critical

test_a2:
  name: 用例a2
  allure:
    feature: demo模块
    story: 用例场景
    title: 用例详细描述a2
    severity: critical

命令行执行用例,并启动allure服务查看报告

> pytest test_a.yml --alluredir ./report
> allure serve ./report

7.12.2 用例有多个步骤,添加step描述

在test_b.yml 用例中有多个步骤:

  • allure 描述可以放到第一个步骤
  • 其它步骤中的name对应的值,就是每个步骤的名称
  • 如果allure 中没有定义feature值,那么默认读取yaml 文件的名称

test_b.yml 示例

config:
  name: 用例描述


test_b2:
-
  allure:
    title: 用例有多个步骤情况
-
  name: 步骤1
  print: hello
-
  name: 步骤2
  print: world

7.12.3 config 中定义公共allure

同一个 yaml 文件中,有多个用例都需加相同的 allure 内容,可以在 config 中定义公共的allure内容 test_c.yml 内容

config:
  name: 用例描述
  allure:
    feature: config是公共的

test_c1:
  name: ss1
  print: hello world

test_c2:
  name: ss1
  allure:
    title: xxxxxxxxx
  print: hello world

test_c3:
  name: ss1
  allure:
    title: yyyyyyyy
  print: hello world

7.13 redis 配置

v1.4.1 新增 redis 数据库配置

7.13.1 redis 基本操作

环境准备

pip instal redis

python 操作redis 基本代码

import redis

# 连上redis
r = redis.StrictRedis(
    host='服务器ip', port=6379, decode_responses=True
)
# 设置一个键值对
r.set('name', 'yoyo')
# 获取结果
print(r.get('name'))   # yoyo

7.13.2 yaml 用例中操作redis

config 配置中配置 REDIS

class Config:
    """每个环境都有一样的公共配置"""
    version = "v1.0"


class TestConfig(Config):
    """测试环境"""
    BASE_URL = 'http://127.0.0.1:8000'
    # ....
    REDIS = {
        "host": '你的redis地址',
        "port": 6379,
        "decode_responses": True

    }
  

class UatConfig(Config):
    """联调环境"""
    BASE_URL =  'http://127.0.0.1:88'
    USERNAME = 'test_uat'
    PASSWORD = '123456'


# 环境关系映射,方便切换多环境配置
env = {
    "test": TestConfig,
    "uat": UatConfig
}

yaml 用例中使用 'redis'对象,调用对应方法如:'redis.get('x')'

config:
  name: x

test_red1:
  name: 测试redis
  variables:
    x: ${redis.get("name")}
  validate:
    - eq: ['${x}', yoyo]

7.14 支持 websocket 协议

v1.4.2 版本支持 websocket 协议

7.14.1 python 操作websocket 协议

环境准备

pip3 install websocket-client
pip3 install websockets

基本代码示例

from websocket import create_connection
import json


url = 'ws://localhost:8081/ws'
ws = create_connection(url, timeout=10)  # 创建链接
print(ws.getstatus())        # 状态码
ws.send('hello world')       # 发送文本消息
print(ws.recv())             # 接收返回内容

# 发送json消息
body = {
    "name": "yoyo",
    "email": "123@qq.com"
}

ws.send(json.dumps(body))
print(ws.getstatus())
print(ws.recv())
ws.shutdown()  # 关闭连接

运行结果

101
接受到的消息是: hello world
101
接受到的消息是: {"name": "yoyo", "email": "123@qq.com"}

7.14.2 yaml 中 webscoket 用例实现

ws关键字相当于create_connection(url, timeout=10) 创建连接 send 关键字相当于 ws.send('hello') 发送文本消息`

test_x1:
  ws:
    url: ws://localhost:8081/ws
    timeout: 10
  send: hello
  validate:
    - eq: [status, 101]
    - eq: [text, '接受到的消息是: hello']

运行结果

collected 1 item                                                                                                        

a_ws/test_ws.yml::test_x1
---------------------------------------------------- live log call -----------------------------------------------------
2023-07-17 19:44:52 [INFO]: 执行文件-> test_ws.yml
2023-07-17 19:44:52 [INFO]: base_url-> http://124.70.221.221:8201
2023-07-17 19:44:52 [INFO]: config variables-> {}
2023-07-17 19:44:52 [INFO]: 运行用例-> test_x1
2023-07-17 19:44:53 [INFO]: 创建 websocket 链接: ws://localhost:8081/ws
2023-07-17 19:44:53 [INFO]: websocket send: hello
2023-07-17 19:44:53 [INFO]: websocket recv: 接受到的消息是: hello
2023-07-17 19:44:53 [INFO]: validate 校验内容-> [{'eq': ['status', 101]}, {'eq': ['text', '接受到的消息是: hello']}]
2023-07-17 19:44:53 [INFO]: validate 校验结果-> eq: [101, 101]
2023-07-17 19:44:53 [INFO]: validate 校验结果-> eq: [接受到的消息是: hello, 接受到的消息是: hello]
PASSED 

websocket 一次连接可以发送多个请求,所以可以在一个用例中有多个send请求

test_x2:
-
  ws:
    url: ws://localhost:8081/ws
    timeout: 10
-
  send: hello
  validate:
    - eq: [status, 101]
    - eq: [text, '接受到的消息是: hello']
-
  send:
    name: yoyo
    email: w22@qq.com
  validate:
    - eq: [status, 101]
    - eq: [text, '接受到的消息是: {"name": "yoyo", "email": "w22@qq.com"}']

7.14.3 多个连接地址测试

一个yaml 文件中可以有多个连接

test_x1:
-
  ws:
    url: ws://localhost:8081/ws
    timeout: 10
-
  send: hello
  validate:
    - eq: [status, 101]
    - eq: [text, '接受到的消息是: hello']
    - eq: [body, '接受到的消息是: hello']

test_x2:
  ws:
    url: ws://localhost:8081/json
    timeout: 10
  send:
    name: xx
    email: w22@qq.com
  validate:
    - eq: [status, 101]
    - eq: [$.name, xx]

多个url地址公共环境地址 base_url 也可以写的config中

config:
  base_url: ws://localhost:8081
test_x1:
-
  ws:
    url: /ws
    timeout: 10
-
  send: hello
  validate:
    - eq: [status, 101]
    - eq: [text, '接受到的消息是: hello']
    - eq: [body, '接受到的消息是: hello']

test_x2:
  ws:
    url: /json
    timeout: 10
  send:
    name: xx
    email: w22@qq.com
  validate:
    - eq: [status, 101]
    - eq: [$.name, xx]

执行结果

collected 2 items                                                                                                       

a_ws/test_ws1.yml::test_x1
-------------------------------live log call ---------------------------------------
2023-07-17 19:50:07 [INFO]: 执行文件-> test_ws1.yml
2023-07-17 19:50:07 [INFO]: base_url-> ws://localhost:8081
2023-07-17 19:50:07 [INFO]: config variables-> {}

2023-07-17 19:50:08 [INFO]: 创建 websocket 链接: ws://localhost:8081/ws
2023-07-17 19:50:08 [INFO]: websocket send: hello
2023-07-17 19:50:08 [INFO]: websocket recv: 接受到的消息是: hello
2023-07-17 19:50:08 [INFO]: validate 校验内容-> [{'eq': ['status', 101]}, {'eq': ['text', '接受到的消息是: hello']}, {'eq
': ['body', '接受到的消息是: hello']}]
2023-07-17 19:50:08 [INFO]: validate 校验结果-> eq: [101, 101]
2023-07-17 19:50:08 [INFO]: validate 校验结果-> eq: [接受到的消息是: hello, 接受到的消息是: hello]
2023-07-17 19:50:08 [INFO]: validate 校验结果-> eq: [接受到的消息是: hello, 接受到的消息是: hello]
PASSED                                                                                      [ 50%]

----------------- live log call -------------------------------------
2023-07-17 19:50:08 [INFO]: 执行文件-> test_ws1.yml
2023-07-17 19:50:08 [INFO]: base_url-> ws://localhost:8081
2023-07-17 19:50:08 [INFO]: config variables-> {}
2023-07-17 19:50:08 [INFO]: 运行用例-> test_x2
2023-07-17 19:50:09 [INFO]: 创建 websocket 链接: ws://localhost:8081/json
2023-07-17 19:50:09 [INFO]: websocket send: {'name': 'xx', 'email': 'w22@qq.com'}
2023-07-17 19:50:09 [INFO]: websocket recv: {"name": "xx", "email": "w22@qq.com", "code": 0, "time": 1689594609.5537014}
2023-07-17 19:50:09 [INFO]: validate 校验内容-> [{'eq': ['status', 101]}, {'eq': ['$.name', 'xx']}]
2023-07-17 19:50:09 [INFO]: validate 校验结果-> eq: [101, 101]
2023-07-17 19:50:09 [INFO]: validate 校验结果-> eq: [xx, xx]
PASSED                                                                                     [100%]

================== 2 passed in 2.47s ============================ 

8.版本变更记录

v1.0.0 发布的第一个版本(已删除)

v1.0.1 发布时间 2022.11.23 可以安装的第一个版本

  • 1.实现基础的 pytest 命令 执行yaml 文件用例功能

v1.0.2 发布时间 2022.11.24

  • 1.新增 extract 关键字,在接口中提取返回结果
  • 2.参数关联,上一个接口的返回值可以作为下个接口的入参

详细功能参阅 extract 关键字文档

v1.0.3 发布时间 2022.11.28

  • 1.config 新增 fixtures 关键字,在yaml 用例中传 fixture 功能和参数化功能
  • 2.config 新增 parameters 用例参数化实现

详细功能参阅 parameters 参数化 关键字文档

v1.0.4 发布时间 2022.11.30

hooks 钩子功能实现

  • 1.request 钩子对请求参数预处理
  • 2.response 钩子对返回结果处理

详细功能参阅 hooks 钩子 关键字文档

v1.0.5 发布时间 2022.12.05

用例分层机制

  • 1.API 层对接口的描述,可以复用
  • 2.Test case 用例层引用API层

v1.0.6 发布时间 2022.12.06

一个yaml 中写多个用例,用例步骤可以不是list 类型

  • 1.config 和 teststeps 不是必须了
  • 2.可以自定义用例名称,用例可以是一个步骤也可以是多个步骤

v1.0.7 发布时间 2022.12.08

新增日志

  • 1.日志默认按日期时间保存到 logs 目录
  • 2.console 日志开启在 pytest.ini 配置,或命令行参数

v1.0.8 发布时间 2022.12.09

结合 allure 生成报告

v1.0.9 发布时间 2022.12.09

全局 base_url 配置

  • 1.pytest.ini 新增 base_url 配置
  • 2.命令行新增 --base-url 参数

v1.1.0 发布时间 2022.12.13

多环境配置切换

  • 1.config.py 实现多环境配置
  • 2.pytest.ini 新增 env 配置, 命令行新增 --env 参数
  • 3.支持 mysql 数据库操作
  • 4.支持 yaml 中引用 env 环境对象

v1.1.1 发布时间 2022.12.14

钉钉机器人通知测试结果

  • 1.config.py 配置钉钉机器人access_token
  • 2.测试结果钉钉群通知
  • 3.支持 requests_function 和 requests_module 内置 fixture 切换

v1.1.2 发布时间 2022.12.16

内置方法提供

  • 1.提供3个常用的内置函数:current_time, rand_value, rand_str
  • 2.一个内置fake对象
  • 3.修复yaml文件为空或格式不正确时,执行报错的问题

v1.1.3 发布时间 2022.12.17

  • 文件上传multipart/form-data 格式支持

v1.1.4 发布时间 2023.2.13

新增3个关键字

  • 1.sleep 添加用例之间的sleep 等待时间
  • 2.skip 跳过用例功能
  • 3.skipif 条件为真时跳过用例

v1.1.5 发布时间 2023.2.16

支持 2 中方式生成 yaml 用例

  • 1.本地 swagger.json 文件
  • 2.在线 swagger.json 地址

v1.1.6 发布时间 2023.2.17

支持2种方式实现

  • 1.在命令行执行的时候带上 --proxies-ip=代理ip:端口
  • 2.在 pytest.ini 添加全局配置--proxies-ip=代理ip:端口

v1.1.8 发布时间 2023.3.17

int 转 str 类型

v1.1.9 发布时间 2023-03-21

做了以下优化

  • 1.validate 校验加了text 关键字获取全部body文本内容
  • 2.用例分层 api和 testcase 层 validate 校验优化,解决之前遗留的bug
  • 3.validate 校验方式更灵活,支持int类型校验字符长度和包含字符
  • 4.log 日志文件优化,只保留最近的5个日志文件

v1.2.0 发布时间 2023-05-08

优化以下问题

  • 1.断言的时候 None 和 'None' 可以判断是相等,在yaml中可以写null 或者 None, 不区分类型了
  • 2.添加局部变量variables
  • 3.优化request 下的hook 功能
  • 4.其它细节优化

v1.2.1 发布时间 2023-05-20

优化以下问题

  • 1.兼容python3.8, python3.9, python3.10版本
  • 2.支持在case 用例中针对单个用例的参数化了
  • 3.参数化数据支持读取外部文件,文件格式可以支持:txt/csv/json/yaml
  • 4.函数的参数可以引用变量了,如: ${fun("hello ${key}")}
  • 5.内置to_json() 函数,字典转 json

v1.2.3 版本 1.解决用例中,hooks单独写request 报错问题 ---已发布的1.2.0解决 2.内置to_json() 函数,字典转 json 4.export 导出功能, config 和 case 中都能添加

v1.2.4 版本 1.新增飞书机器人报告 2.统计报告添加skip 统计

v1.2.5 版本 1.函数的参数可以引用变量了,如: ${fun("hello ${key}")} 2.兼容python3.8, python3.9, python3.10版本

v1.2.6 1.解决与 pytest-base-url 插件不兼容问题 2.解决len_eq 断言 list 长度问题 3.模板过滤器 filter 支持

v1.2.7 1.解决请求钩子函数中传环境配置问题 2.新增内置fixture environ 返回当前运行环境对象

v1.2.8 1.解决用例全部 skip 报错问题 2.解决文件上传参数全部传 files 不生效问题 3.数据库配置支持 DB_INFO 参数传字典类型 4.jmespath 表达式支持length 等函数的提取

v1.2.9 发布

1.优化参数化路径读取,以项目根路径拼接路径 2.优化len_eq 3.变量取值优化

v1.3.0

1.mark 标记实现,config 和用例中都能加 mark

v1.3.1

1.用例超时功能需安装 pytest-runtime-yoyo 插件 2.mark: runtime(1)标记用例运行超时,单位是秒

v1.3.2 1.企业微信机器人通知

v1.3.3 1.添加--start-project 创建项目demo

v1.3.4 1.录制功能

v1.3.5 1.录制功能,支持 multipart/form-data 2.优化录制 yaml 格式用例

v1.3.6 1.export 支持局部变量提升 2.validate 和 extract 支持自定义函数提取

v1.3.7 1.parameters 参数化支持模块级别

v1.3.8 1.parameters 参数化格式重新定义,支持笛卡尔积

v1.3.9 1.log 日志新增只收集用例失败日志

v1.4.0 allure 报告动态添加

v1.4.1 支持redis 配置 支持配置多数据库

v1.4.2 支持 websocket 协议

9 联系我们

作者-上海悠悠 微信/QQ交流:283340479 blog地址 https://www.cnblogs.com/yoyoketang/

完整视频教程地址https://study.163.com/course/courseMain.htm?courseId=1213419817&share=2&shareId=480000002230338 (视频收费:包含 pytest + yaml 框架开发教学和使用教学, 买课程的学员可以用新版本更多强大功能,咨询wx:283340479)

空文件

简介

Python + pytest + yaml + allure + log + 钉钉/飞书、企微群通知 +mysql/redis+ swagger.json 自动生成 yaml 接口用例+录制yaml用例+mock。本框架优势是pip install 安装插件,仅需一个yaml 文件即可运行用例。 展开 收起
Python
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Python
1
https://gitee.com/panwumei/pytest-yaml-yoyo.git
git@gitee.com:panwumei/pytest-yaml-yoyo.git
panwumei
pytest-yaml-yoyo
pytest-yaml-yoyo
master

搜索帮助