• 接口自动化测试之HttpRunner测试框架


    引言

    接口自动化测试的实现方案有很多,没有编程基础的可以使用 Postman+Newman 或 Jmeter+Ant 来实现,有编程基础的则可以结合自动化测试框架来实现。基于Python的测试框架有:Unittest、HttpRunner、Robot Framework、Pytest等,本文主要是讲解HttpRunner,是当下比较流行且实用的框架,可实现自动化测试、性能测试、线上监控、持续集成等多种测试需求。
    注:
    本文篇幅较长,主要是展示了HttpRunner的框架特性,流程、结构,用例组织和快速上手教程。如果消化了本文的内容,基本上可以入门了,接下来就是需要不断的大量实践了

    一、HttpRunner的介绍

    HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试、性能测试、线上监控、持续集成等多种测试需求

    二、HttpRunner的设计理念

    1、追求不重复造轮子
    2、遵循 约定大于配置 的准则
    3、追求投入产出比

    三、HttpRunner的核心特性

    1、继承 Requests 的全部特性
    2、采用 YAML/JSON 的形式描述测试场景,保障测试用例描述的统一性和可维护性
    3、借助辅助函数(debugtalk.py),在测试脚本中轻松实现复杂的动态计算逻辑
    4、支持完善的测试用例分层机制,充分实现测试用例的复用
    5、测试前后支持完善的 hook 机制
    6、响应结果支持丰富的校验机制
    7、基于 HAR 实现接口录制和用例生成功能(har2case)
    8、结合 Locust 框架,无需额外的工作即可实现分布式性能测试
    9、执行方式采用 CLI 调用,可与 Jenkins 等持续集成工具完美结合
    10、测试结果统计报告简洁清晰,附带详尽统计信息和日志记录
    11、极强的可扩展性,轻松实现二次开发和 Web 平台化

    四、HttpRunner的框架流程

    在这里插入图片描述

    五、HttpRunner快速上手

    1、环境准备

    (1)、运行环境:建议使用 Python 3.4 及以上版本
    (2)、安装方式:pip install httprunner
    (3)、安装校验:打开cmd,输入以下命令进行校验,出现版本号则说明安装成功

    1. C:\Users\luoluo>hrun -V
    2. 2.5.7
    1. C:\Users\luoluo>har2case -V
    2. 0.3.1

    2、脚本录制

    为了简化测试用例的编写工作,可以通过抓包工具,如Fiddler、Charles 等进行接口录制,然后导出为标准通用的 HAR 格式(HTTP Archive),再通过 HttpRunner 将 HAR 格式的数据包转换为YAML/JSON格式的测试用例文件,对应的转换工具为:har2case
    下面以Fiddler为例,进行脚本录制和导出
    操作步骤:
    选中需要转换的接口(可多选或全选),点击菜单中的Flie->Export Sessions->Selected session(选择HTTP Archive v1.1类型)保存至本地即可
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    用Charles录制的方法大致相同,操作步骤为:选中需要转换的接口(可多选或全选),点击右键,在悬浮的菜单目录中点击【Export...】,格式选择HTTP Archive(.har)后保存即可

    3、脚本生成

    (1)、将HAR转换为默认的JSON脚本,转换命令为:har2case filename.har

    1. C:\Users\luoluo\Desktop>har2case test1.har
    2. INFO:root:Start to generate testcase.
    3. INFO:root:dump testcase to JSON format.
    4. INFO:root:Generate JSON testcase successfully: test1.json

    (2)、将HAR转换为YAML脚本,转换命令为:har2case -2y filename.har

    1. C:\Users\luoluo\Desktop>har2case -2y test1.har
    2. INFO:root:Start to generate testcase.
    3. INFO:root:dump testcase to YAML format.
    4. INFO:root:Generate YAML testcase successfully: test1.yml

    此时可以看到本地已经生成了对应格式的文件
    在这里插入图片描述
    我们分别来打开看一下里面的内容,由于隐私问题,暂时把实际域名做了修改:
    下面代码是.json格式的,

    1. {
    2. "config": {
    3. "name": "testcase description",
    4. "variables": {}
    5. },
    6. "teststeps": [
    7. {
    8. "name": "/api/trend/webinfo/getnavs",
    9. "request": {
    10. "url": "{{HOST}}/api/trend/webinfo/getnavs",
    11. "params": {
    12. "gender_id": "72105",
    13. "no_cache": "1"
    14. },
    15. "method": "GET",
    16. "headers": {
    17. "Sec-Fetch-Dest": "empty",
    18. "Authorization": "",
    19. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
    20. "Content-Type": "application/json;charset=utf-8;",
    21. "Sec-Fetch-Site": "cross-site",
    22. "Sec-Fetch-Mode": "cors",
    23. "If-None-Match": "W/\"9b200f8fd6b4e23ffd2c77a44e50ba275fdb123c\""
    24. }
    25. },
    26. "validate": [
    27. {
    28. "eq": [
    29. "status_code",
    30. 200
    31. ]
    32. },
    33. {
    34. "eq": [
    35. "headers.Content-Type",
    36. "application/json"
    37. ]
    38. },
    39. {
    40. "eq": [
    41. "content.status_code",
    42. 200
    43. ]
    44. },
    45. {
    46. "eq": [
    47. "content.message",
    48. "ok"
    49. ]
    50. }
    51. ]
    52. },
    53. {
    54. "name": "/api/trend/search/get-recommand",
    55. "request": {
    56. "url": "{{HOST}}/api/trend/search/get-recommand",
    57. "params": {
    58. "gender_id": "72105"
    59. },
    60. "method": "GET",
    61. "headers": {
    62. "Sec-Fetch-Dest": "empty",
    63. "Authorization": "",
    64. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
    65. "Content-Type": "application/json;charset=utf-8;",
    66. "Sec-Fetch-Site": "cross-site",
    67. "Sec-Fetch-Mode": "cors",
    68. "If-None-Match": "W/\"c404d82a8ca9667a6e891907cc6ac7f17edc5143\""
    69. }
    70. },
    71. "validate": [
    72. {
    73. "eq": [
    74. "status_code",
    75. 200
    76. ]
    77. },
    78. {
    79. "eq": [
    80. "headers.Content-Type",
    81. "application/json"
    82. ]
    83. },
    84. {
    85. "eq": [
    86. "content.status_code",
    87. 200
    88. ]
    89. },
    90. {
    91. "eq": [
    92. "content.message",
    93. "ok"
    94. ]
    95. }
    96. ]
    97. },
    98. ]
    99. }

    下面是.yml格式的:

    1. config:
    2. name: testcase description
    3. variables: {}
    4. teststeps:
    5. - name: /api/trend/webinfo/getnavs
    6. request:
    7. headers:
    8. Authorization: ''
    9. Content-Type: application/json;charset=utf-8;
    10. If-None-Match: W/"9b200f8fd6b4e23ffd2c77a44e50ba275fdb123c"
    11. Sec-Fetch-Dest: empty
    12. Sec-Fetch-Mode: cors
    13. Sec-Fetch-Site: cross-site
    14. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
    15. (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
    16. method: GET
    17. params:
    18. gender_id: '72105'
    19. no_cache: '1'
    20. url: ${ENV(HOST)}/api/trend/webinfo/getnavs
    21. validate:
    22. - eq:
    23. - status_code
    24. - 200
    25. - eq:
    26. - headers.Content-Type
    27. - application/json
    28. - eq:
    29. - content.status_code
    30. - 200
    31. - eq:
    32. - content.message
    33. - ok
    34. - name: /api/trend/search/get-recommand
    35. request:
    36. headers:
    37. Authorization: ''
    38. Content-Type: application/json;charset=utf-8;
    39. If-None-Match: W/"c404d82a8ca9667a6e891907cc6ac7f17edc5143"
    40. Sec-Fetch-Dest: empty
    41. Sec-Fetch-Mode: cors
    42. Sec-Fetch-Site: cross-site
    43. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
    44. (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
    45. method: GET
    46. params:
    47. gender_id: '72105'
    48. url: ${ENV(HOST)}/api/trend/search/get-recommand
    49. validate:
    50. - eq:
    51. - status_code
    52. - 200
    53. - eq:
    54. - headers.Content-Type
    55. - application/json
    56. - eq:
    57. - content.status_code
    58. - 200
    59. - eq:
    60. - content.message
    61. - ok

    从中可以看出,yaml格式的看起来更简洁些

    4、执行脚本

    执行脚本的命令为:hrun filename.json 或hrun filename.yml
    下面是执行情况 ,由于隐私问题,暂时把实际域名做了修改:

    1. C:\Users\luoluo\Desktop>hrun test1.yml
    2. INFO HttpRunner version: 2.5.7
    3. INFO Start to run testcase: testcase description
    4. /api/trend/webinfo/getnavs
    5. INFO GET https://{{HOST}}/api/trend/webinfo/getnavs
    6. INFO status_code: 200, response_time(ms): 524.28 ms, response_length: 5357 bytes
    7. .
    8. /api/trend/search/get-recommand
    9. INFO GET https://{{HOST}}//api/trend/search/get-recommand
    10. INFO status_code: 200, response_time(ms): 62.19 ms, response_length: 232 bytes
    11. .
    12. ----------------------------------------------------------------------
    13. Ran 2 tests in 0.600s
    14. OK
    15. INFO Start to render Html report ...
    16. INFO Generated Html report: C:\Users\luoluo\Desktop\reports\20200414T125709.244873.html
    17. Sentry is attempting to send 0 pending error messages
    18. Waiting up to 2 seconds
    19. Press Ctrl-Break to quit
    20. C:\Users\luoluo\Desktop>

    5、查看报告

    我们可以直接复制上面的报告地址,查看测试报告,报告内容很详尽:
    在这里插入图片描述
    点击log按钮,可以查看具体某个接口的请求和返回数据
    在这里插入图片描述

    6、创建项目

    至此,你可以开始创建一个新的项目,来进行更多的实操吧
    创建项目命令:hrun --startproject 新项目名称

    1. C:\Users\luoluo\Desktop>hrun --startproject myobject
    2. Start to create new project: myobject
    3. CWD: C:\Users\luoluo\Desktop
    4. created folder: myobject
    5. created folder: myobject\api
    6. created folder: myobject\testcases
    7. created folder: myobject\testsuites
    8. created folder: myobject\reports
    9. created file: myobject\api\demo_api.yml
    10. created file: myobject\testcases\demo_testcase.yml
    11. created file: myobject\testsuites\demo_testsuite.yml
    12. created file: myobject\debugtalk.py
    13. created file: myobject\.env
    14. created file: myobject\.gitignore
    15. C:\Users\luoluo\Desktop>

    项目的文件组织结构如下:
    在这里插入图片描述

    • 此处是条承上启下的分割线========================================================================

    以上的内容主要是通过录制方式为你快速打开HttpRunner的大门,相信到了这一阶段,你肯定会想要对录制的脚本进行优化改造,以更好的满足你的测试需求。比如因为登录后会获取token,而后续的所有接口都需要传这个token的时候,你就需要设置全局变量;当测试步骤不变,但需要传入不同的请求参数时,你就需要做请求参数化;你需要对响应结果进行断言优化;你还需要……
    这些问题都要如何解决呢?接下来的内容将带你深入了解HttpRunner测试框架流程结构,厘清测试用例的组织思路,同时细化到每个文件的脚本编写......
    让我们一起来开启HttpRunner的探索之旅吧!

    六、HttpRunner的项目结构

    1、项目文件结构

    以下为项目文件目录样例:
    myobject
    ├── .env
    ├── data
    │ ├── acount.csv
    ├── api
    │ ├── demo_api.yml
    ├── debugtalk.py
    ├── reports
    │ ├── 1550999327.html
    ├── testcases
    │ ├──demo_testcase.yml
    └── testsuites
    │ ├──demo_testsuite.yml

    2、文件类型说明

    • .env:项目环境变量,用于存储敏感信息
      编写格式:key=value
      引用方式:在YAML/JSON脚本中,直接使用内置函数ENV引用,即$

    • debugtalk.py:项目中逻辑运算辅助函数
      ① 编写常见功能和业务相关的函数,使得测试用例中可以引用函数,实现一些动态的计算逻辑,如获取时间戳、生成随机数、处理签名等
      ② 编写hook函数,使得测试用例执行前后可以调用钩子函数,实现测试的准备和清理工作,如初始化某个请求参数、修改响应结果的某个值等

      引用方式:在 YAML/JSON 格式的测试用例中,通过${func()}的方式引用
      注:
      ① 文件存在时,其所在目录被视为项目工程根目录
      ② 该文件不存在时,运行测试的所在路径被视为项目工程根目录

    • api:接口定义描述

    • testcases:存放测试用例

    • testsuites:存放测试用例集

    • data:存放测试数据

    • reports:存放测试报告

    七、HttpRunner的测试用例组织

    1、编写接口描述(api)

    (1)模板样例demo_api.yml

    1. name: demo api
    2. variables:
    3. var1: value1
    4. var2: value2
    5. request:
    6. url: /api/path/${var1}
    7. method: POST
    8. headers:
    9. Content-Type: "application/json"
    10. json:
    11. key: ${var2}
    12. validate:
    13. - eq: ["status_code", 200]

    (2)模板说明

    • name:接口名称,命名可随意,没有什么作用
    • variables:变量信息,格式为key: value(注意冒号后面有一个空格,这是ymal文件规定的格式)
    • request:请求信息
      url:请求地址
      method:请求方法
      header:请求头
      json:请求参数 ,格式为key: value,value一般引用变量或函数,格式为:${var}、
      $
    • validate:结果断言

    2、编写测试用例(testcases)

    (1)模板样例demo_testcase.yml

    1. config:
    2. name: "demo testcase"
    3. variables:
    4. device_sn: "ABC"
    5. username: ${ENV(USERNAME)}
    6. password: ${ENV(PASSWORD)}
    7. base_url: "http://127.0.0.1:5000"
    8. teststeps:
    9. -
    10. name: demo step 1
    11. api: path/to/api1.yml
    12. variables:
    13. user_agent: 'iOS/10.3'
    14. device_sn: $device_sn
    15. extract:
    16. - token: content.token
    17. validate:
    18. - eq: ["status_code", 200]
    19. -
    20. name: demo step 2
    21. api: path/to/api2.yml
    22. variables:
    23. token: $token

    (2)模板说明

    • config:整个测试用例的全局配置
      name:测试用例名称,测试报告会显示该名称
      variables:全局变量
      base_url:访问地址,一般写域名host
    • teststeps:测试步骤,下面以列表形式编写一个或多个测试步骤,并且每一个测试步骤前面有‘-’
      name:测试步骤名称,测试报告会显示该名称
      api:引用接口描述文件,相对路径从根目录开始
      variables:局部变量,优先于全局变量,若此处填写了局部变量,则会覆盖config中的variables全局变量
      extract:从响应结果中提取参数,并保存到变量中,给其他测试步骤引用
      validate:结果断言

    (3)测试用例说明

    ① 每个测试用例都是一个独立用例,原则上不依赖其他测试用例,不考虑执行顺序
    ② extract结果断言的字段有:status_code、content、text,json、cookies、elapsed、headers、reason、enconding、ok、url
    若响应结果为json格式,可以通过content.xxx.0.id获取id(content为响应内容,xxx和id为字典的key,0为数组第一位);
    若响应结果为xml/html,可通过正则表达式来获取结果参数
    ③ 校验器有:eq(=)、lt(<)、le(<=)、gt(>)、ge(>=)、ne(!=)、str_eq、len_eq、len_gt、len_ge、len_lt、len_le、contains、contained_by、type_match、regex_match、startswith、endswith

    3、编写测试用例集(testsuites)

    (1)模板样例demo_testsuite.yml

    1. config:
    2. name: "demo testsuite"
    3. variables:
    4. device_sn: "XYZ"
    5. base_url: "http://127.0.0.1:5000"
    6. testcases:
    7. -
    8. name: call demo_testcase with data 1
    9. parameters:
    10. phone-password: ${P(data/acount.csv)}
    11. testcase: path/to/demo_testcase.yml
    12. variables:
    13. device_sn: $device_sn
    14. -
    15. name: call demo_testcase with data 2
    16. testcase: path/to/demo_testcase.yml
    17. variables:
    18. device_sn: $device_sn

    (2)模板说明

    • config:全局配置项
      name:测试用例集的名称
    • testcases:测试用例的集合
      name:测试用例名称
      parameters:参数化,有多种实现方式,具体参考下文中的参数化数据驱动(data)
      testcase:引用测试用例文件,相对路径从根目录开始
      variables:环境变量

    (3)测试用例集说明

    测试用例集的testcases是1个数组,数组的每个值为1个测试用例,一般有name和testcase两个参数,除此之外还可以进行参数化数据驱动

    特别注意:yml文件需遵循严格的缩进,缩进多少格无所谓,只要保证同一层级的key对齐即可

    八、HttpRunner的参数化数据驱动(data)

    1、参数化方式

    有三种方式,以下以登录为例:
    (1)、在 YAML/JSON 中直接指定参数列表:该种方式最为简单易用,适合参数列表比较小的情况

    1. config:
    2. name: "login testsuites"
    3. testcases:
    4. -
    5. name: login_with_account by raw_list
    6. parameters:
    7. phone-password:
    8. - ['1342388xxxx',123456]
    9. - ['1342388yyyy',654321]
    10. testcase: path/to/demo_testcase.yml

    (2)、通过内置的 parameterize(可简写为P)函数引用 CSV 文件:该种方式需要准备 CSV 数据文件,适合数据量比较大的情况

    1. config:
    2. name: "login testsuites"
    3. testcases:
    4. -
    5. name: login_with_account by csv_file
    6. parameters:
    7. phone-password: ${P(data/account.csv)}
    8. testcase: path/to/demo_testcase.yml

    account.csv 文件里面的内容如下:

    phone,password
    1342388xxxx,123456
    1342388yyyy,654321

    (3)、调用 debugtalk.py 中自定义的函数生成参数列表:该种方式最为灵活,可通过自定义 Python 函数实现任意场景的数据驱动机制,当需要动态生成参数列表时也需要选择该种方式

    1. config:
    2. name: "login testsuites"
    3. testcases:
    4. -
    5. name: login_with_account by custom_function
    6. parameters:
    7. phone-password: ${get_account()}
    8. testcase: path/to/demo_testcase.yml

    在debugtalk.py 文件里面自定义函数如下:

    1. def get_account():
    2. return [
    3. {"phone":"1342388xxxx","password":"123456"},
    4. {"phone":"1342388yyyy","password":"654321"},
    5. ]

    九、HttpRunner的测试用例分层

    在自动化测试领域,自动化测试用例的可维护性是极其重要的因素,测试用例分层机制的核心是将接口定义、测试步骤、测试用例、测试场景进行分离,单独进行描述和维护,从而尽可能地减少自动化测试用例的维护成本
    图片来自HtttpRunner官方说明文档
    几个核心概念:

    ① 测试用例(testcase)应该是完整且独立的,每条测试用例应该是都可以独立运行的
    ② 测试用例是测试步骤(teststep)的 有序 集合,每一个测试步骤对应一个 API 的请求描述
    ③ 测试用例集(testsuite)是测试用例的 无序 集合,集合中的测试用例应该都是相互独立,不存在先后依赖关系的;如果确实存在先后依赖关系,那就需要在测试用例中完成依赖的处理

    十、HttpRunner的hook机制

    HttpRunner的hook机制的概念相当于Unittest框架里面前置( setUp )和后置( tearDown )处理器,即:前置 setup_hooks 和后置 teardown_hooks 函数

    1、编写hook函数

    (1)、测试用例层面(testcase)
    setup_hooks: 在整个用例开始执行前触发 hook 函数,主要用于准备工作
    teardown_hooks: 在整个用例结束执行后触发 hook 函数,主要用于测试后的清理工作
    (2)、测试步骤层面(teststep)
    setup_hooks: 在 HTTP 请求发送前执行 hook 函数,主要用于准备工作;也可以实现对请求的 request 内容进行预处理
    teardown_hooks: 在 HTTP 请求发送后执行 hook 函数,主要用于测试后的清理工作;也可以实现对响应的 response 进行修改,例如进行加解密等处理

    在 debugtalk.py 文件中编写hook函数:

    1. import time
    2. def sleep(n_secs):
    3. time.sleep(n_secs)

    2、调用hook函数

    hook 函数的定义放置在项目的 debugtalk.py 中,在 YAML/JSON 中调用 hook 函数采用 :${func(a)}的形式,a为函数里面的变量。
    下面的代码,通过 setup_hooks 和 teardown_hooks 关键词来调用 debugtalk.py 文件中的sleep函数,根据位置来决定是测试用例层面还是测试步骤层面,实现前置和后置的操作

    1. config:
    2. name: "demo testcase"
    3. variables:
    4. device_sn: "ABC"
    5. username: ${ENV(USERNAME)}
    6. password: ${ENV(PASSWORD)}
    7. base_url: "http://127.0.0.1:5000"
    8. # 测试用例层面,实现测试用例执行前等待10秒,执行后等待10秒的操作
    9. setup_hooks:
    10. - ${sleep(10)}
    11. teardown_hooks:
    12. - ${sleep(10)}
    13. teststeps:
    14. -
    15. name: demo step 1
    16. api: path/to/api1.yml
    17. variables:
    18. user_agent: 'iOS/10.3'
    19. device_sn: $device_sn
    20. # 测试步骤层面,实现测试步骤执行前等待10秒,执行后等待10秒的操作
    21. setup_hooks:
    22. - ${sleep(5)}
    23. teardown_hooks:
    24. - ${sleep(5)}
    25. extract:
    26. - token: content.token
    27. validate:
    28. - eq: ["status_code", 200]
    29. -
    30. name: demo step 2
    31. api: path/to/api2.yml
    32. variables:
    33. token: $token

    十一、更多探索

    据说HttpRunner测试框架是国内的一位测试大牛写的,有中文使用文档,官方文档可查阅

    Python接口自动化测试零基础入门到精通(2023最新版)

  • 相关阅读:
    C语言描述数据结构 —— 常见排序(2)冒泡排序、快速排序
    用HTML、CSS和JS打造绚丽的雪花飘落效果
    HR人才测评,什么是成就导向?如何测评成就导向?
    什么是哈希算法?什么是哈希冲突以及怎样解决哈希冲突?
    容器技术 — Docker 容器引擎运行原理解析与实践
    Spring实战 | Spring AOP核心秘笈之葵花宝典
    安全防御——一、防火墙的基本概念
    【Linux系统管理】14 日志管理
    Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第12章 Vue3.X新特性解析 12.9 Refs 模板
    SPI通信协议
  • 原文地址:https://blog.csdn.net/dad22211/article/details/133893006