使用HttpRunner 3.x实现接口自动化测试

HttpRunner介绍

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

特点

  • 继承了Requests的全部特性,可轻松实现 HTTP(S) 的各种测试需求
  • 使用YAML或JSON格式定义测试用例,并使用Pytest以简洁优雅的方式运行用例
  • 支持使用HAR实现接口录制并生成测试用例
  • 支持variables/ extract/ validate/hooks机制,以应对非常复杂的测试场景
  • 使用debugtalk.py插件自定义函数,可以在测试用例的任何部分调用
  • 使用Jmespath,更加方便对返回的json进行校验
  • 通过Pytest的强大插件生态补充了httprunner的功能
  • 使用Allure,让测试报告更加美观,可读性更强
  • 通过与locust的结合,可以很方便利用httprunner进行接口性能测试
  • 支持CLI命令,更可与持续集成工具(CI/CD)完美结合,如Jenkins

HttpRunner安装

pip install httprunner	# 安装httprunner,若下载失败请使用国内源下载
hrun -V		# 查看httprunner版本
# 版本显示正确表示安装完成,执行以下命令,通常安装httprunner时会一并安装,若不存在请通过pip命令单独安装
har2case -V	# 查看har2case版本
locusts -V	# 查看locust版本

安装完成后以下5个命令会自动写入系统环境变量中,无需手动配置

  • httprunner:主命令,用于所有功能

  • hrun:指令httprunner run的别名,用于运行YAML/JSON/Pytest 测试用例

  • hmake: 指令httprunner make的别名,将YAML/JSON用例转换成pytest用例

  • har2case:指令httprunner har2case的别名,将HAR文件转换成 YAML/JSON 用例

  • locust: 利用locust运行性能测试

YAML介绍

HttpRunner3.x支持三种格式的测试用例:YAML、JSON以及Pytest(python)

以YAML为例,先简单介绍下YAML,YAML支持注释、换行、多行字符串、单行字符串等,比JSON更简洁、更直观、可读性更高

YAML的语法规则:

  • 大小写敏感(区分大小写)
  • 使用缩进表示层级关系
  • 不允许使用Tab键,只允许使用空格键进行缩进
  • 缩进的空格数量无要求,但相同层级关系左侧必须对齐
  • 使用#表示注释

支持的数据类型:

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)

    • key: # 键值之间的冒号后面需要有空格
          child-key: value
          child-key2: value2
      
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)

    • - 		# 以 - 开头的行表示构成一个数组,如下为两组数据
       - A	# 第一组数据只有A,A又独为一组
      -		# 第二组数据有B和C,B和C又各为一组
       - B
       - C
      
  • 纯量(scalars):单个的、不可再分的值

    • # 字符串、布尔值、整数、浮点数、时间等属于纯量
      string:
          - 这是字符串
          - 'Hello world'  #可以使用双引号或者单引号包裹特殊字符
          - newline
            newline2    #字符串可以拆成多行,每一行会被转化成一个空格
      int:
          - 123
          - 0b1010_0111_0100_1010_1110    #二进制表示
      

    引用

&用来建立锚点,<< 表示合并到当前数据,* 用来引用锚点

defaults: &default # 建立锚点,数据变更时只需修改此锚点,引用锚点无需修改,减少修改量
  adapter:  postgres
  host:     localhost
development:
  database: myapp_development
  <<: *default # 引用锚点并合并到当前数据,避免数据重复
test:
  <<: *default # 引用锚点并合并到当前数据

HttpRunner使用

可通过先快速搭建个脚手架,使用命令httprunner startproject 项目名称,例如在PyCharm终端输入httprunner startproject APItesting就会生成如下图中的项目结构

请添加图片描述

用例结构及编写

以实际项目为例,请通过注释了解测试用例结构及编写方法

# Secgroup.yml文件
config: # 全局配置,每个测试用例都必须有config部分
    name: 安全组业务流接口测试  # 用例描述,name是必须要有的,其它配置项为可选项,根据实际项目增删
    base_url: http://192.166.66.22  # 项目路径,亦可配置在环境配置env文件中
    variables:  # 参数配置
        Opertype: ADD
        poolId: e3cc3d8b90b4465998ddfec569a66612
        resType: SECURITYGROUP
        name: dyd${
    
    random_num()}  # 此处调用了在debugtalk.py中定义的生成随机数的函数
        secGType: SECURITY_VM
        adapterType: SINGLE
        approvalType: SECURITYGROUP-ADD

teststeps:  # 每个测试用例都有1个或多个测试步骤,name,request是必须有的,其它根据实际项目配置
-   name: 登录平台  # 步骤一,登录平台
    request:  # 配置请求信息,(请求方法、url、头信息、传参等)
        method: POST
        url: /users/login  # 因全局配置中已存在base_url,故在测试步骤不必填写,其会自动拼接
        headers:
            SID: dy5eabbbf8eb496dd7788664f8322c8e
        json: # 入参参数
            username: ${
    
    ENV(username)}  # 引用env环境配置中的账号和密码
            password: ${
    
    ENV(password)}
    extract:  # 提取响应数据中的access_token值
        access_token: body.entity.access_token # # body是响应体json的根节点
    validate: # 判断是否成功,可使用多个断言
      - eq: [body.success,true]
      - eq: [status_code, 200]

-   name: 创建安全组 # 步骤二,创建安全组,每个步骤格式都差不多,根据实际项目做修改即可
    request:
        method: POST
        url: /dy/adapter
        headers: &header # 建立锚点 
            Authorization: Bearer $access_token # 调用步骤一登录后获得的token
            SID: dy5eabbbf8eb496dd7788664f8322c8e
        json: # 入参参数,也可直接写json格式
          adapterTargets:
            - level: $Opertype # 调用全局配置中的参数
              poolId: $poolId
              resourceType: $resType
              resourceProperties:
                name: $name
                securityGroupType: $secGType
          adapterType: $adapterType
          approvalType: $approvalType
    validate: # 断言是否成功
      - eq: [body.success,true]
      - eq: [status_code, 200]

-   name: 查看安全组列表 # 步骤三,查看列表是否新增创建的安全组
    request:
        method: POST
        url: /dian/security-groups
        headers:
            <<: *header # 引用锚点并合并到headers下
        json: # 传参
            pageNum: 1
            pageSize: 5
    extract:  # 提取列表中第一个安全组的id,有用到下标查找
        secg_id: body.entity.list.0.id
    validate: # 断言
      - eq: [body.success,true]
      - eq: [status_code, 200]
      - contains: [body.entity.list.0.name,dyd] # 判断第一条安全组的名字是否包含创建时的关键字

-   name: 查看安全组详情 # 步骤四,查看新创建的安全组详情
    request:
        method: GET
        url: /dian/security-groups/$secg_id # 调用步骤三中提取的安全组id并与url拼接
        headers:
            <<: *header # 引用锚点并合并到headers下
    validate: # 断言
      - eq: [body.success,true]
      - eq: [status_code, 200]
      - contains: [body.entity.name,dyd] # 判断安全组详情中的安全组名称是否包含dyd

上面示例中用到了env文件和debugtalk.py文件

通常在自动化测试项目的根目录中创建 .env 文件,并将敏感数据信息放置到其中,存储采用 name=value 的格式,通过脚手架生成的接口测试项目中,会自动生成.env文件,可直接在.env中添加环境变量,.env文件中除注释外,其它行都不允许存在空格,引用环境变量的方法${ENV(name)}

请添加图片描述

debugtalk.py文件名称不能变更,通过脚手架生成的接口测试项目中也会自动生成,在此文件中写python脚本,然后在YAML文件中调用,调用方法${func()},例如上文中用到的随机数${random_num()}

import random
def random_num():
    return random.randint(1,10)
参数化

支持多种参数化方式,如.env、debugtalk.py、csv等,此处仅介绍Pytest中的参数化方式,如下示例:

# login.yml文件
config:
    name: 参数化测试登录接口
    parameters: #  多个参数具有关联性的参数需要将其定义在一起,采用短横线(-)进行链接
        user-pwd-msg:
          - ["admin","VCfa……gnw==",null]
          - ["nimda","VCfa……gnw==","用户不存在或者已冻结"]
          - ["admin","123456","解密失败,请确认传输加密方式"]
       # username: ["admin","nimda","adnim"] 单个参数参数方式
teststeps: 
  - name: 登录验证
    request:
        method: POST
        url: ${
    
    ENV(HOST)}/users/login # 引用env环境配置中的HOST
        headers:
            SID: dy5eabbbf8eb496dd7788664f8322c8e
        json:
            username: $user # 调用全局配置中的参数进行传参
            password: $pwd
    validate:
      - eq: [ body.message,$msg ] #调用全局配置中的msg参数,判断是否与响应结果一致
      - eq: [ body.message,$msg ]
      - eq: [ body.message,$msg ]
hook机制

hook机制可以在请求前和请求后调用钩子函数,跟Pytest中的setup/teardown类似,hook机制分为两个层级:

(1)测试用例层面:在YAML/JSON测试用例的config中新增关键字setup_hooks和teardown_hooks

  • setup_hooks: 在整个用例开始执行前触发hook函数,主要用于准备工作
  • teardown_hooks: 在整个用例结束执行后触发hook函数,主要用于测试后的清理工作

(2)测试步骤层面:在YAML/JSON测试步骤teststeps中新增关键字setup_hooks和teardown_hooks

  • setup_hooks: 在请求发送前执行hook函数,主要用于准备工作;也可实现对请求的request内容进行预处理
  • teardown_hooks: 在请求发送后执行hook函数,主要用于测试后的清理工作;也可实现对响应的response进行修改,例如进行加解密等处理
编写hook函数

​ hook函数的定义是放置在项目的debugtalk.py中,在YAML/JSON中调用hook函数同样是采用${func($a, $b)}的形式,如下示例

# debugtalk.py中增加以下代码
def setupHook(request):
    print("**********开始执行测试用例**********")
    print("request请求:{}".format(request))

def teardownHook(response):
    print("**********修改响应结果后再进行断言**********")
    response.status_code = 300
    response.body["success"] = "false"
# get_token.yml文件
config:
    name: 获取token认证信息
teststeps:
  - name: 获取token
    setup_hooks:	# 调用hook函数
      - ${
    
    setupHook($request)}
    teardown_hooks:
      - ${
    
    teardownHook($response)}
    request:
        method: POST
        url: ${
    
    ENV(HOST)}/oauth/token # 引用env环境配置中的HOST
        headers:
            SID: dy5eabbbf8eb496dd7788664f8322c8e
        data:
            username: admin
            password: 123456
    extract:
      token: body.entity.access_token # 提取响应结果中的token值
    validate:
      - eq: [status_code,200]
      - eq: [body.success,true]

执行后能够获得正确结果,但是因为teardown_hooks在断言前把响应结果改了,所以会返回断言失败的报错信息

请添加图片描述

可以使用requests库在debugtalk.py写请求接口,获取token,然后可通过${func()}调用,也可通过setup_hooks调用,实际情况需根据接口而定

# debugtalk.py中增加以下代码
import requests
def getToken():
    url = "http://192.166.66.22/oauth/token"
    header = {
    
    "SID": "dy5eabbbf8eb496dd7788664f8322c8e"}
    data = {
    
    "username": "admin",
            "password": "123456"}
    res = requests.post(url,headers=header,data=data).json()
    return res["entity"]["access_token"]
# keypair.yml文件
config:
    name: 测试获取SSH密钥列表接口
teststeps:
-   name: 获取SSH密钥列表
    request:
        headers: &header # 建立锚点
            Authorization: Bearer ${
    
    getToken()} # 调用debugtalk.py中的getToken()
            SID: dy5eabbbf8eb496dd7788664f8322c8e
        json:
            pageNum: 1
            pageSize: 10
        method: POST
        url: ${
    
    ENV(HOST)}/dian/keypairs # 引用env环境配置中的HOST
    validate: # 断言
      - eq: [body.success,true]
-   name: 上传密钥文件
    request:
        method: POST
        url: ${
    
    ENV(HOST)}/dian/keypairs/file # 引用env环境配置中的HOST
        headers:
            <<: *header # 引用锚点并合并到当前数据
        upload: # 上传文件
            file: "data/csb-dyd.pem"
            field: "value"
    validate:  # 断言
        - eq: [ status_code, 200 ]
用例执行
hrun testcases/Secgroup.yml 						# 运行指定路径的用例
hrun testcases/get_token.yml testcases/keypair.yml	# 运行指定路径的多个用例
hrun testcases	# 运行testcases目录下所有的用例

hrun先将YAML/JSON转为pytest(python)格式,然后再运行pytest,所以运行yml文件后会自动在同一目录下生成pytest文件,生成的pytest用例名增加_test后缀,且.yml/yaml/.json替换为.py

以后可以直接通过pytest命令执行.py格式的测试用例,hrun也可以直接使用pytest的参数,比如hrun -sv login.yml,以下示例是上文中参数化示例执行后生成的.py文件内容

import pytest
from httprunner import Parameters
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase

class TestCaseLogin(HttpRunner):
    @pytest.mark.parametrize(
        "param",
        Parameters(
            {
    
    
                "username-password-msg": [
                    ["admin","VCfa……gnw==",None],
                    ["nimda","VCfa……gnw==","用户不存在或者已冻结"],
                    ["admin", "123456", "解密失败,请确认传输加密方式"],
                ]
            }
        ),
    )
    def test_start(self, param):
        super().test_start(param)

    config = Config("登录")

    teststeps = [
        Step(
            RunRequest("获取token")
            .post("${ENV(HOST)}/users/login")
            .with_headers(**{
    
    "SID": "bf5eadddf8eb496bb7788474f8322c6e"})
            .with_json({
    
    "username": "$username", "password": "$password"})
            .validate()
            .assert_equal("body.message", "$msg")
            .assert_equal("body.message", "$msg")
            .assert_equal("body.message", "$msg")
        ),
    ]

if __name__ == "__main__":
    TestCaseLogin().test_start()
测试报告

由于httprunner集成了pytest,所以可使用pytest-htmlallure-pytest报告,pytest-html是内置html报告,可直接使用

hrun testcases --html=report.html # 生成pytest-html格式的报告

请添加图片描述

allure-pytest需要单独安装pip install allure-pytest

hrun testcases --alluredir=/report/allure	# 生成中间结果
allure serve /report/allure					# 在线打开报告
allure generate report/allure -o report/allure/html	# 生成html报告

请添加图片描述

压力测试

locust详细使用方法后续单独开篇,此处简单介绍一下,命令为locusts -f file.py,使用-f指定YAML/JSON格式的测试用例时,会首先转换为pytest,然后运行负载测试,所以可以直接压测测试接口时已自动生成的.py格式的文件,下图为压测结果的部分截图

请添加图片描述

录制与生成测试用例
导出HAR文件

对于没有详细接口文档,需要抓包进行测试的接口,可以通过导出har文件然后转为YAML格式进行测试,Charles和Fiddler都可以导出,如下图所示,使用Fiddler导出时,需注意接口所使用的协议版本

请添加图片描述

生成测试用例

可以使用命令har2case将HAR文件转成测试用例

har2case login.har		# 默认转为json格式
har2case login.har -2y	# -2y表示转为yaml格式

下面是转换为yaml格式的内容,通常是需要修改的,比如:断言,关联关系、参数化等都是需要修改完善的

config:
    name: testcase description
    variables: {
    
    }
teststeps:
-   name: /user/doLogin
    request:
        data:
            pwd: '123456'
            user: admin
        headers:
            Content-Type: application/x-www-form-urlencoded; charset=UTF-8
            User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
                (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
            X-Requested-With: XMLHttpRequest
        method: POST
        url: http://192.166.66.22:8080/user/doLogin
    validate:
    -   eq:
        - status_code
        - 200
    -   eq:
        - headers.Content-Type
        - application/json;charset=UTF-8
    -   eq:
        - content.user_id
        - 1
    -   eq:
        - content.state
        - ok
    -   eq:
        - content.gotoUrl
        - /ucenter

对生成的测试用例进行修改完善后就可以执行测试啦

猜你喜欢

转载自blog.csdn.net/Q0717168/article/details/122093995