Package: pytest-yaml-yoyo
基于 httprunner 框架的用例结构，我自己开发了一个pytest + yaml 的框架，那么是不是重复造轮子呢？
不可否认 httprunner 框架设计非常优秀，但是也有缺点，httprunner3.x 的版本虽然也是基于 pytest 框架设计，结合 yaml 执行用例，但是会生成一个py文件去执行。
在辅助函数的引用也很局限，只能获取函数的返回值，不能在 yaml 中对返回值重新二次取值。
那么我的这个框架，就是为了解决这些痛点。。。。本插件可以实现以下优势：1、基于 pytest 框架安装插件即可使用，环境非常简单2、只需要写 yaml 文件用例即可运行，使用 pytest 运行的命令3、extract 提取结果支持 jmespath、jsonpath、re 正则提取实现参数关联4、validate 丰富的校验方法5、全局仅登录一次，在用例中自动在请求头部添加 Authentication token认证6、用例参数化 parameters 功能实现7、export 提升变量为 session 会话级别，可以跨 yaml 文件传参8、yaml 中调用 fixture 功能实现9、yaml 中调用辅助函数功能使用10、yaml 中调用 hooks 功能11、用例分层机制：API 和用例层12、支持 logging 日志13、支持 allure 报告14、支持 mysql 数据库增删改查15、支持钉钉/飞书机器人通知测试结果和 allure 报告地址16、支持生成随机测试数据，如字符串，姓名，手机号，邮箱等17、根据 swagger.json 自动生成 yaml 文件接口用例18、支持全局代理配置联系我们作者-上海悠悠 微信/QQ交流:283340479blog地址https://www.cnblogs.com/yoyoketang/视频教学https://study.163.com/course/courseMain.htm?courseId=1213419817&share=2&shareId=480000002230338版本变更记录v1.0.0
发布的第一个版本（已删除)v1.0.1  发布时间 2022.11.23
可以安装的第一个版本1.实现基础的 pytest 命令 执行yaml 文件用例功能v1.0.2 发布时间 2022.11.241.新增extract 关键字，在接口中提取返回结果2.参数关联，上一个接口的返回值可以作为下个接口的入参详细功能参阅 extract 关键字文档v1.0.3 发布时间 2022.11.281.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.支持mysql 数据库操作v1.1.1 发布时间 2022.12.14钉钉机器人通知测试结果1.config.py 配置钉钉机器人access_token2.测试结果钉钉群通知3.支持 requests_function 和 requests_module 内置 fixture 切换v1.1.2 发布时间 2022.12.16内置方法提供1.提供3个常用的内置函数：current_time， rand_value， rand_str2.一个内置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.8 发布时间 2023.3.17int 转 str 类型v1.1.9 发布时间 2023-03-21做了以下优化1.validate 校验加了text 关键字获取全部body文本内容2.用例分层 api和 testcase 层 validate 校验优化，解决之前遗留的bug3.validate 校验方式更灵活，支持int类型校验字符长度和包含字符4.log 日志文件优化，只保留最近的5个日志文件v1.2.0 发布时间 2023-05-08优化以下问题1.断言的时候 None 和 ‘None’ 可以判断是相等，在yaml中可以写null 或者 None, 不区分类型了2.添加局部变量variables3.优化request 下的hook 功能4.其它细节优化v1.2.1 发布时间 2023-05-20优化以下问题1.兼容python3.8, python3.9, python3.10版本2.支持在case 用例中针对单个用例的参数化了3.参数化数据支持读取外部文件，文件格式可以支持：txt/csv/json/yaml4.函数的参数可以引用变量了，如: ${fun(“hello ${key}”)}5.内置to_json() 函数，字典转 jsonv1.2.2 发布优化以下问题1.解决与 pytest-base-url 不兼容问题2.解决len_eq 断言 list 长度问题v1.2.3 发布 2023-05-29优化以下问题1.解决请求钩子函数中传环境配置问题2.新增内置fixture environ 返回当前运行环境对象3.报告总结加skip通过数量v1.2.4 发布 2023-05-30优化以下问题1.解决用例全部 skip 报错问题2.解决文件上传参数全部传 files 不生效问题3.数据库配置支持 DB_INFO 参数传字典类型4.jmespath 表达式支持 length 等函数的提取v1.2.5 发布 2023-06-07优化以下问题1.优化参数化路径读取，以项目根路径拼接路径v1.2.6 发布 2023-06-08优化以下问题1.优化断言方式 len_eq2.模板过滤器 filter 支持3.变量取值优化, 所有的取值，数字类型默认 int4.取值结果数字转字符串，可以用${str(取值表达式)}或者过滤器方式${取值表达式 | str}v1.2.7 发布 2023-06-191.添加–start-project命令创建项目 demoInstallation / 安装最佳环境体验Python 3.8, 3.9. 3.10 版本Pytest 7.2.0+pip 安装插件pip install pytest-yaml-yoyoUsage / 快速创建 demo 项目使用–start-project命令快速创建 demo 项目pytest --start-project执行后会生成如下项目结构D:\demo\
├── case_demo/
│   ├── test_extract.yml
│   ├── test_get.yml
│   ├── test_post.yml
├── config.py
├── pytest.ini通过 pytest 命令可以执行用例pytestUsage / 第一个 hello worldyaml 用例编写规则，跟 pytest 识别默认规则一样，必须是 test 开头的，以`.yml` 结尾的文件才会被识别新建一个`test_hello.yml`文件config:
  name: yy

teststeps:
-
  name: demo
  print: hello world用例整体结构延续了 httprunner 框架的用例结果，主要是为了大家快速上手，减少新的规则学习config  是必须的里面必须有 name 用例名称，base_url 和 variables 是可选的teststeps 用例的步骤，用例步骤是一个array 数组类型，可以有多个步骤从上面的运行可以看出，request 不是必须的，我们可以直接调用python内置函数print 去打印一些内容了。一个简单的 http 请求以`http://www.example.com/` get 请求示例
test_get_demo.ymlconfig:
  name: get

teststeps:
-
  name: get
  request:
    method: GET
    url: http://httpbin.org/get
  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 ========================再来一个post请求test_post_demo.ymlconfig:
  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]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"
}校验方式延续了httprunner的校验语法，可以支持response取值对象：status_code, url, ok, headers, cookies, text, json, encoding
其中返回的是json格式，那么可以支持jmespath 取值语法:body.json.usernamejsonpath 语法:$..usernamere 正则语法如果返回的不是json格式，那么可以用正则取值变量的声明与引用变量的声明，只支持在 config 声明整个yml文件的全局变量（不支持单个step的变量，减少学习成本）
在 httprunner 里面变量引用语法是$user, 引用函数是`${function()}`
我这里统一改成了一个语法变量引用${var}和 引用函数`${function()}`
（表面上没多大变量，实际上功能强大了很多，使用了强大的 jinja2 模板引擎)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]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]参数关联上一个接口提取到了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/postextract 提取结果二次取值我们在前面提到不能在yaml中对返回值重新二次取值。,
这也是一些同学提到的问题，对于提取的结果，我想继续取值，比如他是一个字符串，在python中可以用切片取值
那么，在 yaml 中如何实现？我重新设计的这个框架中，就可以支持python语法，直接用切片取值headers:
      url: ${url[:4]}用例分层当我们测试流程类的接口，需反复去调用同一个接口，就会想到复用API，在代码里面可以写成函数去调用。
那么在yaml 文件中，我们可以把单个API写到一个yaml 文件，测试用例去调用导入API。我这里只分2层：API 层 和 Test case 用例层API 层: 描述接口request请求，可以带上validate 基本的校验Test case 用例层: 用例层多个步骤按顺序引用APIAPI 层示例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命名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重新定义 yaml 用例格式一个yaml 文件中可以写多个用例，每个用例相当于 pytest 的一个函数，
用例名称最好是test开头，如果不是test开头，也会自动拼接成test开头的示例test1:
    name: 用例1
    print: hello 11111

test2:
    name: 用例2
    print: hello 22222

test3:
    name: 用例3
    print: hello 3333为了框架的可扩展性，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也可以是一个listtest1:
 -
    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]logging 日志pytest 的日志分2个部分：console 控制台输出的日志log_file  保存到本地文件的日志本插件默认情况下会记录运行日志保存在项目根目录logs下，以当前时间保存txt文本日志内容。
日志默认保存info级别。
console 控制台默认不输出日志开启 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------自定义 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自定义保存日志文件本插件默认情况下会记录运行日志保存在项目根目录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命令行参数配置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=INFOallure  报告本插件是基于pytest框架开发的，所以pytest 的插件都能使用，生成报告可以用到 allure 报告
allure 报告功能在 v1.0.8 版本上实现allure 命令行工具allure 是一个命令行工具，需要去github上下载最新版[https://github.com/allure-framework/allure2/releases](https://github.com/allure-framework/allure2/releases)allure  命令行工具是需要依赖jdk 环境，环境内容自己去搭建了生成 allure 报告在用例所在的目录执行命令,–alluredir是指定报告生成的目录pytest --alluredir ./report打开allure 报告执行命令allure serve ./report全局 base_url一个完整的url 地址由环境地址和接口地址拼接而成，环境地址是可变的，可以部署到测试环境，uat联调环境等不同的环境。
不管部署到哪个环境，接口的地址是不可变的，通常需要一个全局base_url 地址做到环境可切换。
pip 安装插件pip install pytest-yaml-yoyobase_url 全局配置功能在 v1.0.9 版本上实现在接口测试中，通常会把环境 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]全局 base_url 配置从项目的角度讲，测试项目接口的 base_url 都是一样的，所以我们只需全局设置一个就行了，不需要每个yaml 文件中重复去写。
于是可以在 pytest.ini 里面配置全局base_url[pytest]

base_url = http://httpbin.org那么yaml用例就不需要写 base_url 了，默认会引用 pytest.ini 的全局配置