httprunner简要说明

news/2024/10/15 14:56:50

Httprunner

1.简介

面向HTTP(S)协议的测试通用框架,维护YAML/JSON脚本执行测试用例,最终都是转化为python文件执行,3.0以后官方建议直接转为维护python脚本

特性

  1. 继承Requests特性
  2. 辅助函数debugtalk.py,实现动态计算逻辑
  3. 测试分层,api层、测试用例层、测试套件
  4. 支持Hook机制
  5. 丰富的校验机制
  6. 基于HAR实现接口录制和用例生成功能
  7. 结合locust框架,分布式性能测试
  8. 可与jenkins进行持续集成
  9. 支持测试报告,pytest-html和allure
  10. 可拓展,支持二次开发和平台化

文件

  1. YAML/JSON,测试用例文件,一个文件对应一条测试用例
  2. debugtalk.py,脚本函数,存在时所在目录被视为项目工程的根目录,不存在时运行测试的路径为根目录,测试用例文件和测试报告文件都是基于该目录运行和生成
  3. .env,存储项目全局变量
  4. .csv 项目数据文件,用于数据驱动
  5. report(自动生成) 运行后自动生成,无需创建
  6. testcase 存放测试用例
  7. har 存放导出的文件

2.YAML文件简介

  1. :表示键值对

  2. -表示数组

  3. 纯量:字符串、整数、浮点数、布尔值、NULL、时间、日期

  4. 对象,数组嵌套

    - id: 1name: kong
    -id: 2name: zhenguocity: - 'shandong'- 'jinan'
    

3. 环境安装

pip install httprunner==版本号

hrun -v 查看版本号

httprunner startproject httprunner_demo 创建项目

hrun httprunner_demo 运行

4.快速生成接口测试用例

  • fiddler获取接口.har包

    选中单接口或多接口,File->Export Sessions->selected session->选择HTTPArchive v1.2

  • har转yaml

    har2case name.har -2y

  • har转json

    har2cae name.har -2j

  • har转python

    har2case name.bar

  • 执行:yaml和json 直接hrun name.yaml,python文件也可以使用pytest

5.脚本文件详解

yaml

  • get

    - config:  # 配置信息name: '测试'  # 测试用例名称base_url: 'url'  # ip地址- test: name: '第一步测试' # 测试步骤名称request: url: '/login' # 路径method: GETheaders:Accept: 'text/html'params:name: ''age: ''validate:  # 断言- eq: ['status_code',200]- eq: [content.expires_in,7200] # content表示接口响应的json
    
  • post

    - config:  # 配置信息name: '测试'  # 测试用例名称base_url: 'url'  # ip地址- test: name: '第一步测试' # 测试步骤名称request: url: '/login' # 路径method: POSTheaders:Content-Type: 'application/json'json:{"name": {"age": 18}}validate:  # 断言- eq: ['status_code',200]- eq: [content.expires_in,7200] # content表示接口响应的json
    

python

  • get

    # NOTE: Generated By HttpRunner v3.1.4
    # FROM: har/baidu_home.harfrom httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase# 一个类是一个testcase,继承自HTTpRunner
    class TestCaseBaiduHome(HttpRunner):config = Config("testcase description").verify(False).base-url('ip地址').variables(**{"name": "123"}).export(*["token", "list"])# 配置测试用例设置,包括url、验证、变量、导出"""Config(): 显示在执行日志和测试报告中base_url() 主机ip,用于切换测试环境variables() 公共变量,级别比Step中的低,同名不执行verify 用来决定是否验证服务器TLS证书的开关。通常设置为False,当请求https请求时,就会跳过验证。如果你运行时候发现抛错SSLError,可以检查一下是不是verify没传,或者设置了True。export() 导出的变量** 解构字典,*结构列表或元组"""teststeps = [# teststeps 每个Step对应一个API请求,也可以调用另一个testcaseStep(RunRequest("/s")  # 指定测试用例名称,显示在日志和测试报告中.get("https://www.baidu.com/s").call(导入的测试用例名称) # 导入后可使用其中的变量.with_variables(**{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}) # 测试用例变量,会覆盖全局变量中重名的变量.with_params(**{"ie": "utf-8","mod": "1","isbd": "1","isid": "C9FF25725AB54698","f": "8","rsv_bp": "1","rsv_idx": "1","tn": "baidu","wd": "httprunner","fenlei": "256","oq": "httprunner%20%26lt%3B","rsv_pq": "86a39119000039fe","rsv_t": "9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ++oE9TlK6y2o+O7A7XdDS6Yus","rqlang": "cn","rsv_enter": "0","rsv_dl": "tb","rsv_sug3": "2","rsv_sug1": "2","rsv_sug7": "000","rsv_btype": "t","prefixsug": "httprunner","rsp": "1","inputT": "6648","rsv_sug4": "7252","rsv_sug": "2","bs": "httprunner 3","rsv_sid": "undefined","_ss": "1","clist": "","hsug": "httprunner 3\thttprunner","f4s": "1","csor": "10","_cr1": "40730",}).with_headers(**{"Host": "www.baidu.com","Connection": "keep-alive","Accept": "*/*","is_xhr": "1","X-Requested-With": "XMLHttpRequest","is_referer": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner%203&fenlei=256&oq=httprunner%203&rsv_pq=86a39119000039fe&rsv_t=2b6c1PBdGIcDYzEKyW9BkMzeCPMYcfbqTSf%2FEDXZuefGUrmcy2q1pxhJ0NQ&rqlang=cn","User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36","Sec-Fetch-Site": "same-origin","Sec-Fetch-Mode": "cors","Sec-Fetch-Dest": "empty","Referer": "https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=httprunner&fenlei=256&oq=httprunner%2520%2526lt%253B&rsv_pq=86a39119000039fe&rsv_t=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus&rqlang=cn&rsv_enter=0&rsv_dl=tb&rsv_sug3=2&rsv_sug1=2&rsv_sug7=000&rsv_btype=t&prefixsug=httprunner&rsp=1&inputT=6648&rsv_sug4=7252&rsv_sug=2","Accept-Encoding": "gzip, deflate, br","Accept-Language": "zh-CN,zh;q=0.9","Cookie": "BIDUPSID=EA49B0E234E0F93BBD3C0082A586CDEA; PSTM=1619952293; BAIDUID=C9FF25B24E5A3C59C96D61DB506725AB:FG=1; BD_UPN=123253; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; H_PS_PSSID=33986_33819_33849_33759_33675_33607_26350_33996; H_PS_645EC=9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus; delPer=0; BD_CK_SAM=1; PSINO=5; BDSVRTM=14; WWW_ST=1620121549937",}).with_cookies(**{"BIDUPSID": "EA49B0E234E0F93BBD3C0082A586CDEA","PSTM": "1619952293","BAIDUID": "C9FF25B24E5A3C59C96D61DB506725AB:FG=1","BD_UPN": "123253","BDORZ": "B490B5EBF6F3CD402E515D22BCDA1598","H_PS_PSSID": "33986_33819_33849_33759_33675_33607_26350_33996","H_PS_645EC": "9d65Kx91ldJ2V3LDLjZmstZTQ27dNOYAMJ%2B%2BoE9TlK6y2o%2BO7A7XdDS6Yus","delPer": "0","BD_CK_SAM": "1","PSINO": "5","BDSVRTM": "14","WWW_ST": "1620121549937",}).validate().assert_equal("status_code", 200).assert_equal('headers."Content-Type"', "text/html;charset=utf-8")),]if __name__ == "__main__":TestCaseBaiduHome().test_start()
    

6.获得响应数据&extract提取值到变量

yaml

  • 提取响应头、响应行

    - test:name: 接口名称 百度接口request:url: /method: GETextract:  # 提取值存储到变量中- code: status_code  # 响应码- info: reason       # ok- header_Content: headers.Content-Type  # 响应头部validate:- eq: [$code,200]  # 引用变量  $变量名- eq: [$info,"OK"]- eq: [$header_Content,'text/html']
    
  • 正则解析相应内容

    - test:name: 百度主页request:url: /method: GETheaders:  # 如果断言为中文的话,加上headers的Accept-Language即可Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36extract:- title: <title>(.+?)</title>  # 可以使用正则表达式提取validate:- eq: [$title,"百度一下,你就知道"]
    
  • 解析响应正文

    - test:name: 百度主页request:url: /cgi-bin/tags/getmethod: GETparams:access_token: 49_lsdk_pQJJ4R5IWdWVcDTQu3bHyVOsHDlAcuA99UtVwsmzrtHhSGJKgSPMi3i3TdOQrGeuzZdB62K1uhcKJQAk6eKjzlBL7HgWvAmw7gfiRTp00QnLdSZzN7ul9f2TMPex-Iz2tCg-ZWsSPLbJTJdABAYIYextract:- id: content.tags.0.id  # content为根节点- name: content.tags.0.namevalidate:- eq: [$id,2]- eq: [$name,"星标组"]
    

python

  • Step(RunTestCase("request with functions").with_variables(**{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}).call(RequestWithFunctions).extract().with_jmespath("body.args.foo2", "foow") # json提取)
    

7.接口关联

当前文件

  • yaml

    - test:name: 获取tokenrequest:url: /cgi-bin/tokenmethod: GETparams:grant_type: client_credentialappid: wxf144190secret: 92a113bd4b5extract:   # 提取变量- token: content.access_token- time: content.expires_invalidate:- eq: [$time,7200]- test:name: 获取用户所有标签request:url: /cgi-bin/tags/getmethod: GETparams:access_token: $token  # 引用上面的token实现关联extract:- id: content.tags.0.id- name: content.tags.0.namevalidate:- eq: [$id,2]- eq: [$name,"星标组"]
    
  • python

    Step(RunTestCase("request with functions").with_variables(**{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}).call(RequestWithFunctions).extract().with_jmespath("body.args.foo2", "foow") # json提取)Step(RunTestCase("request with functions").with_variables(**{"foo1": "testcase_ref_bar1", "expect_foo1": "testcase_ref_bar1"}).with_params(**{"str1": "hello", "str2": "$foow"})  # 在第二个传参中引用导出的变量)
    

跨文件

  • yaml

    test.yml

    - config:  # 配置信息name: '测试'  # 测试用例名称base_url: 'url'  # ip地址export: - token- test: name: '第一步测试' # 测试步骤名称request: url: '/login' # 路径method: GETheaders:Accept: 'text/html'params:name: ''age: ''validate:  # 断言- eq: ['status_code',200]- eq: [content.expires_in,7200] # content表示接口响应的jsonextract:token: content.access_token
    
    - config:  # 配置信息name: '测试'  # 测试用例名称base_url: 'url'  # ip地址- test: name: '第一步测试' # 测试步骤名称testcase: test.yamlextract:- tokenrequest: url: '/login' # 路径method: GETheaders:Accept: 'text/html'params:name: ''age: ''validate:  # 断言- eq: ['status_code',200]- eq: [content.expires_in,7200] # content表示接口响应的json- eq: [content, $token]
    
  • python

    testcases.test_getUserName_demo

    from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCaseclass TestCaseRequestWithGetUserName(HttpRunner):config = (Config("test /getUserName").base_url("http://localhost:5000").verify(False).export(*["username"])  # 这里定义出要导出的变量)teststeps = [Step(RunRequest("getUserName").get("/getUserName").extract().with_jmespath("body.username", "username")  # 提取出目标值,赋值给username变量.validate().assert_equal("body.username", "chenshifeng")),]if __name__ == "__main__":TestCaseRequestWithGetUserName().test_start()
    
    from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
    from testcases.test_getUserName_demo import TestCaseRequestWithGetUserName as RequestWithGetUserName   # 记得要导入引用的类class TestCaseRequestWithJoinStr(HttpRunner):config = (Config("test /joinStr").base_url("http://localhost:5000").verify(False))teststeps = [Step(RunTestCase("setUp getUserName").call(RequestWithGetUserName)   # 导入后就可以调用了.export(*["username"])  # 在RunTestCase步骤中定义这个变量的导出),Step(RunRequest("joinStr").get("/joinStr").with_params(**{"str1": "hello", "str2": "$username"})  # 在第二个传参中引用导出的变量.validate().assert_equal("body.result", "hello $username")   # 断言的预期值也引用变量),]if __name__ == "__main__":TestCaseRequestWithJoinStr().test_start()
    

8.断言

yaml

validate:- eq: [status_code,200]# 断言
- config:name: 测试百度网站base_url: https://www.baidu.com- test:name: 接口名称 百度接口request:url: /method: GETvalidate:- eq: [status_code,200]  # 判断相等的4种写法 [实际结果,预期结果]- is: [status_code,200]- ==: [status_code,200]- equals: [status_code,200]- eq: ["${函数名()}", "结果"] # 函数需要引号

eq、equals、==、is,判断实际结果和期望结果是否相等
lt、less_than,判断实际结果小于期望结果
le、less_than_or_equals,判断实际结果小于等于期望结果
gt、greater_than,判断实际结果大于期望结果
ge、greater_than_or_equals,判断实际结果大于等于期望结果
ne、not_equals, 判断实际结果和期望结果不相等
str_eq、string_equals 判断转字符串后对比实际结果和期望结果是否相等
len_eq、length_equals、count_eq 判断字符串或list长度
len_gt、length_greater_than、count_gt、count_greater_than 判断实际结果的长度大于和期望结果
len_ge、length_greater_than_or_equals、count_ge、count_greater_than_or_equals实际结果的长度大于等于期望结果
len_lt、length_less_than、count_lt、count_less_than实际结果的长度小于期望结果
len_le、length_less_than_or_equals、count_le count_less_than_or_equals实际结果的长度小于等于期望结果

9.环境变量

存放在.env文件中,格式为 变量名 = 变量值

${变量名}调用

10.辅助函数debufralk.py

在执行文件中引入该文件函数

import randomdef get_value():return "猫咪"def get_search_word():work = [1, 2, 3, 4]num = random.randint(0, len(work)-1)return num
# 调用 debugtalk.py文件中的函数
- config:name: 百度主页base_url: https://www.baidu.comexport:- title- test:name: 百度搜索request:url: /smethod: GETheaders:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36params:wd: ${get_value()} # 引用函数extract:- title: <title>(.+?)</title>validate:- eq: [$title,"猫_百度搜索"]

11.hook机制,初始化和清理

debugtalk.py

def setup_case():print("测试执行")def teardown_case():print("测试结束")
- config:name: 百度主页base_url: https://www.baidu.comoutput:- title# 放到用例层级setup_hooks:- ${setup_case()}teardown_hooks:- ${teardown_case()}

12.忽略跳过测试用例

  • skip: 无条件跳过

  • skipif: 条件成立跳过

  • skipUnless: 条件不成立跳过

    # skip是用来忽略跳过测试用例
    - config:name: 百度主页base_url: https://www.baidu.comoutput:- title- test:name: 百度搜索# 忽略跳过用例只能在测试步骤中使用skip: 无条件跳过
    #    skipIf: True         # 条件为 True 时跳过
    #    skipUnless: False    # 条件为 False 时跳过request:url: /smethod: GETheaders:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36params:wd: 猫extract:- title: <title>(.+?)</title>validate:- eq: [$title,"猫_百度搜索"]
    

13.测试分层

api层

  • 对接口进行独立管理

    get_access_token.yml

    name: 123
    base_url: http:...
    request:url:method: params:name: 13
    validate: - eq: [status_code, 2000]
    

testcase(测试用例)

  • 测试用例管理

  • demo1_test.yml

    - config:name: 测试export:- token- test:name: 测试用例名称api: ../get_access_token.yml # 引用接口validate: - eq: [content.expires_in, 1000]extract:token: content.access_token
    

引用同层级用例

  • - config:name: 测试export:- token-test:name: 引用同层级接口testcase: ../demo1_test.ymlextract:token # 引入接口变量- test:name: 测试用例名称api: ../get_access_token.yml # 引用接口validate: - eq: [content.expires_in, 1000]extract:token: content.access_token
    

接口用例管理(接口套件)

  • config: name: test_suitetestcases:- name: testcase1testcase: ../testcase1.yml- name: testcase2testcase: ../testcase2.yml# 写法2
    config: - name: test_suitetestcases:testcase1:   # 测试用例名称testcase: ../testcase1.ymltestcase2: testcase: ../testcase2.yml
    

14.中文乱码

  • 添加请求头信息:详情见6.yaml正则提取

  • debugtalk.py解码

    # encode编码   decode解码
    # iso8859-1 编码,解码成 utf-8
    def iso8859_to_utf8(str):return str.encode("iso8859-1").decode("utf-8")# utf-8 编码,解码成 iso8859-1
    def utf8_to_iso8859(str):return str.encode("utf-8").decode("iso8859-1")# unicode_escape 编码,解码成 utf-8
    def unicode_escape_to_utf8(str):return str.encode("unicode_escape").decode("utf-8")
    

15.参数化传递(多组数据)

yaml

variables关键字

套件层传给用例层再传给api层

test_suite.yml

config: name: test_suitetestcases:testcase1:testcase: ../testcase1.ymlvariables:search_word: 猫猫 # 参数名: 值

test_case.yml

- config:name: 测试export:- token- test:name: 测试用例名称api: ../get_access_token.yml # 引用接口variables:work: $search_workvalidate: - eq: [content.expires_in, 1000]extract:token: content.access_token

test_api.yml

name: 123
base_url: http:...
request:url:method: wd: $workparams:name: 13
validate: - eq: [status_code, 2000]

parameters关键字

接受多个参数,依次执行

config: name: test_suitetestcases:testcase1:testcase: ../testcase1.ymlparameters:search_word: ["猫猫", "狗狗", "兔兔"] # 参数名: 值
- config:name: 测试export:- token- test:name: 测试用例名称api: ../get_access_token.yml # 引用接口cariables:work: $search_workvalidate: - eq: [content.expires_in, 1000]- eq: ["猫", $result]  # $result调用套件层变量的值extract:token: content.access_token
name: 123
base_url: http:...
request:url:method: wd: $workparams:name: 13
validate: - eq: [status_code, 2000]

dubugtalk.py自定义函数

def search_key():return [["猫","猫_百度搜索"],["狗","狗_百度搜索"],["大象","大象_百度搜索"]]
config: name: test_suitetestcases:testcase1:testcase: ../testcase1.ymlparameters:search_word: ${search_key()}

用例层和接口层不变

- config:name: 测试export:- token- test:name: 测试用例名称api: ../get_access_token.yml # 引用接口cariables:work: $search_workvalidate: - eq: [content.expires_in, 1000]- eq: [123, $result] # result是内部变量extract:token: content.access_token

csv参数化

search, result
1, 2
3, 4
config: name: test_suitetestcases:testcase1:testcase: ../testcase1.ymlparameters:search_word: ${P(文件路径)}

可在钩子函数中使用Faker或者pymysql构建测试数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/71840.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

上手protobuf:一文掌握protobuf的使用

简介 在上一篇文章中,我们已经了解了protobuf是什么,还有proto文件该如何编写 在本文中,将着重讲讲protobuf该怎么用 Protobuf使用教程 Protobuf是一种高效数据序列化协议,可支持多种编程语言,不同编程语言使用方法或略有差异、或有多种方法,以下主要介绍大致的、通用的使…

预算不变,数据安全大升级!揭秘TRAID+的神奇之处

假期过后,公司弥漫着一股低沉的气息,公司的CTO,小威,正面对着一个令人头疼的问题——如何在不增加预算的情况下,提高公司的数据安全性。 如今各种勒索病毒层出不穷,或是因为硬盘的损坏或者员工的马虎使数据丢失都会让公司损失巨大。 俗话说鸡蛋不要放在一个篮子里。面对着…

第六章

学号3020 6.2点击查看代码 edges = [ ("Pe", "T", 13), ("Pe", "N", 68), ("Pe", "M", 78), ("Pe", "L", 51), ("Pe", "Pa", 51), ("T", "N"…

FingersGestures 简介

FingersGestures 简介 FingersGestures为官方推荐手势插件,主要用于替代EasyTouch,EasyTouch很多年没有更新了,并且已经在官方资产商店下架,FingersGestures插件在官方商店具有很高的热度和持续的版本迭代,以及提供了二十多个实例进行参考,后续考虑项目中都使用此插件。 …

TextMeshPro

简介支持图文混排和矢量字体图文混排使用需要开启RichText 需要挂载SpriteAsset(不挂载的情况下,使用默认的spriteAsset) 使用下标来显示没有设置spriteAsset的情况设置了spriteAsset的情况

TowardsDataScience-博客中文翻译-2020-五十五-

TowardsDataScience 博客中文翻译 2020(五十五)原文:TowardsDataScience Blog 协议:CC BY-NC-SA 4.0GPT-3,OpenAI 的革命原文:https://towardsdatascience.com/gpt-3-openais-revolution-f549bf3d4b25?source=collection_archive---------43-----------------------来源…

约80%开发效率提升,原生鸿蒙政务、文旅行业样板间专区上线

10月8日,华为官方正式宣布,其最新操作系统HarmonyOS NEXT于当日10:08正式开启公测。 为有效助力开发者加速行业应用开发,华为开发者联盟生态市场(简称生态市场)近日上线了原生鸿蒙政务行业、文旅行业“样板间”专区。政务和文旅行业作为数字化转型的重要领域,对数智应用的…

TowardsDataScience-博客中文翻译-2020-四十五-

TowardsDataScience 博客中文翻译 2020(四十五)原文:TowardsDataScience Blog 协议:CC BY-NC-SA 4.0基于 Avro 模式的类固醇弹性研究原文:https://towardsdatascience.com/elasticsearch-on-steroids-with-avro-schemas-3bfc483e3b30?source=collection_archive---------…