一、Har简介
- Har格式是指HTTP归档格式(HTTP Archive Format)。
- 用于记录HTTP会话信息的文件格式。
- 多个浏览器都可以生成 Har 格式的数据。
二、应用场景
- 通过 Har 格式的接口数据,转换为接口自动化测试脚本:
- 提升脚本的编写效率;
- 降低脚本的出BUG的几率;
三、实现思路
- 读取Har数据,获取到request内容。
- 提前准备测试用例模版,读取模板文件,将模板中的数据进行替换。
- 将读取的Har数据写入到模板中,替换后的模板写入测试用例文件。
四、模板技术
1、什么是Mustache
-
Mustache是一种轻量级的模板语言。
-
需要定义模板,然后可以将数据填充到设定好的位置。
-
python的第三方库:
- pystache:
pip install pystache
; - chevron:
pip install chevron
;–更推荐使用: chevron is fast Chevron runs in less than half the time of pystache (Which is not even up to date on the spec). And in about 70% the time of Stache (A ‘trimmed’ version of mustache, also not spec compliant).
- pystache:
2、模板技术使用
-
pystache库:
pystache.render("origin_data",{"key":"value"}
; -
chevron库:
chevron.render("origin_data",{"key":"value"}
; -
参数说明:
- 参数一:使用
{{key}}
占位的字符串数据; - 参数二:需要替换进去的键值对数据,key为占位的变量,value为需要替换进去的值;
- 参数一:使用
五、使用har生成接口测试用例
1、常用模板
(1)Junit接口测试用例模板
package com.ceshiren.har;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
public class HttpbinTest {
@Test
void httpbinReq() {
given()
// 可以设置测试预设
.when()
// 发起 GET 请求
.get("{{url}}")
.then()
// 解析结果
.log().all() // 打印完整响应信息
.statusCode(200); // 响应断言
}
}
(2)httprunner 的用例模版
config:
name: basic test with httpbin
teststeps:
-
name: httprunner的模板
request:
url: {{url}}
method: GET
validate_script:
- "assert status_code == 200"
3、python 接口测试用例模板
import requests
def test_request():
r = requests.get(url="{{url}}")
2、生成python(pytest)接口测试用例模板
- 读取Har数据,获取到request内容。
- 提前准备测试用例模版,读取模板文件,将模板中的数据进行替换。
- 将读取的Har数据写入到模板中,替换后的模板写入测试用例文件。
pytest.template
import requests
def test_case_{{title}}():
# 发起请求,json参数根据实际情况自行传入参数,get请求用params传入,post请求如果是formdata用data传入,如果是json就用json传入
response = requests.request(method="{{method}}",url="{{url}}",json=)
# 断言-状态码
assert response.status_code == 200
har2case.py
import json
import chevron
from jsonpath import jsonpath
class GenerateCase:
# 读取har文件获取到request内容
def __load_har(self,har_filename):
with open(har_filename,'r',encoding='utf8') as f:
har_dict = json.load(f)
# 返回request内容
return jsonpath(har_dict,"$..request")[0]
# 以har中数据替换模板中的变量并以模板生成测试用例
def generate_case_by_template(self,template_filename,case_filename,har_filename):
# 1、获取har中的request数据
request_data = self.__load_har(har_filename)
method = request_data["method"]
url = request_data["url"]
title = url.split("/")[len(url.split("/"))-1]
# title = url.
# 2、读取模板文件,将模板中的数据进行替换
with open(template_filename,'r',encoding='utf8') as f:
template = f.read()
# 替换
case_content = chevron.render(template,{"method":method,"url":url,"title":title})
# 3、替换后的模板写入测试用例文件
with open(case_filename,'w',encoding='utf8') as f:
f.write(case_content)
if __name__ == '__main__':
har_filename = "../data/litemall.hogwarts.ceshiren.com.har"
case_filename = "../testcase/test_login.py"
template_filename = "../data/pytest.template"
GenerateCase().generate_case_by_template(template_filename,case_filename,har_filename)
litemall.hogwarts.ceshiren.com.har
{
"log": {
"version": "1.2",
"creator": {
"name": "WebInspector",
"version": "537.36"
},
"pages": [],
"entries": [
{
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "",
"scriptId": "10",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-27643b19.a4fcf60f.js",
"lineNumber": 0,
"columnNumber": 22235
},
{
"functionName": "xhr",
"scriptId": "10",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-27643b19.a4fcf60f.js",
"lineNumber": 0,
"columnNumber": 19825
},
{
"functionName": "lt",
"scriptId": "10",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-27643b19.a4fcf60f.js",
"lineNumber": 0,
"columnNumber": 23252
}
],
"parent": {
"description": "Promise.then",
"callFrames": [
{
"functionName": "request",
"scriptId": "10",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-27643b19.a4fcf60f.js",
"lineNumber": 0,
"columnNumber": 26854
},
{
"functionName": "",
"scriptId": "10",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-27643b19.a4fcf60f.js",
"lineNumber": 0,
"columnNumber": 180
},
{
"functionName": "Mt",
"scriptId": "10",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-27643b19.a4fcf60f.js",
"lineNumber": 0,
"columnNumber": 31509
},
{
"functionName": "login",
"scriptId": "72",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-63ca1188.7c7a066f.js",
"lineNumber": 0,
"columnNumber": 5158
},
{
"functionName": "loginSubmit",
"scriptId": "72",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/chunk-63ca1188.7c7a066f.js",
"lineNumber": 0,
"columnNumber": 5458
},
{
"functionName": "c",
"scriptId": "9",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/app.3b7a29df.js",
"lineNumber": 28,
"columnNumber": 86885
},
{
"functionName": "k",
"scriptId": "9",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/app.3b7a29df.js",
"lineNumber": 28,
"columnNumber": 84859
},
{
"functionName": "Qe",
"scriptId": "9",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/app.3b7a29df.js",
"lineNumber": 12,
"columnNumber": 20570
},
{
"functionName": "n",
"scriptId": "9",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/app.3b7a29df.js",
"lineNumber": 12,
"columnNumber": 10794
},
{
"functionName": "Ci.i._wrapper",
"scriptId": "9",
"url": "https://litemall.hogwarts.ceshiren.com/vue/static/js/app.3b7a29df.js",
"lineNumber": 12,
"columnNumber": 56949
}
]
}
}
},
"_priority": "High",
"_resourceType": "xhr",
"cache": {},
"connection": "954403",
"request": {
"method": "POST",
"url": "https://litemall.hogwarts.ceshiren.com/wx/auth/login",
"httpVersion": "http/2.0",
"headers": [
{
"name": ":authority",
"value": "litemall.hogwarts.ceshiren.com"
},
{
"name": ":method",
"value": "POST"
},
{
"name": ":path",
"value": "/wx/auth/login"
},
{
"name": ":scheme",
"value": "https"
},
{
"name": "accept",
"value": "application/json, text/plain, */*"
},
{
"name": "accept-encoding",
"value": "gzip, deflate, br"
},
{
"name": "accept-language",
"value": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"
},
{
"name": "cache-control",
"value": "no-cache"
},
{
"name": "content-length",
"value": "43"
},
{
"name": "content-type",
"value": "application/json"
},
{
"name": "cookie",
"value": "_ga=GA1.2.123742433.1644288262; sensorsdata2015jssdkcross=%7B%22%24device_id%22%3A%221888985fce60-03d79ec7b95907-7b515473-1327104-1888985fce7fad%22%7D; Hm_lvt_214f62eef822bde113f63fedcab70931=1696156377,1696377764,1696997968,1697074446; _ga_L943NTQ6FJ=GS1.2.1697440574.2.0.1697440574.60.0.0; JSESSIONID=91f733ad-8c18-4142-86cd-08b08dd81c25; _gid=GA1.2.348187596.1697553224; Hm_lpvt_214f62eef822bde113f63fedcab70931=1697696055; _ga_XP6GNLH30P=GS1.2.1697696060.83.1.1697698552.0.0.0"
},
{
"name": "origin",
"value": "https://litemall.hogwarts.ceshiren.com"
},
{
"name": "pragma",
"value": "no-cache"
},
{
"name": "referer",
"value": "https://litemall.hogwarts.ceshiren.com/vue/index.html"
},
{
"name": "sec-fetch-dest",
"value": "empty"
},
{
"name": "sec-fetch-mode",
"value": "cors"
},
{
"name": "sec-fetch-site",
"value": "same-origin"
},
{
"name": "user-agent",
"value": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/115.0.0.0"
},
{
"name": "x-litemall-token",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0aGlzIGlzIGxpdGVtYWxsIHRva2VuIiwiYXVkIjoiTUlOSUFQUCIsImlzcyI6IkxJVEVNQUxMIiwiZXhwIjoxNjk3MjU3NzU3LCJ1c2VySWQiOjQwOTk2OSwiaWF0IjoxNjk3MjUwNTU3fQ.hm4pFgM--OzVLbeDgTy26-aFIHUvqCkfzyyp804JE3A"
}
],
"queryString": [],
"cookies": [
{
"name": "_ga",
"value": "GA1.2.123742433.1644288262",
"path": "/",
"domain": ".ceshiren.com",
"expires": "2024-11-22T06:14:16.174Z",
"httpOnly": false,
"secure": false
},
{
"name": "sensorsdata2015jssdkcross",
"value": "%7B%22%24device_id%22%3A%221888985fce60-03d79ec7b95907-7b515473-1327104-1888985fce7fad%22%7D",
"path": "/",
"domain": ".ceshiren.com",
"expires": "2024-11-22T06:07:54.029Z",
"httpOnly": false,
"secure": false
},
{
"name": "Hm_lvt_214f62eef822bde113f63fedcab70931",
"value": "1696156377,1696377764,1696997968,1697074446",
"path": "/",
"domain": ".ceshiren.com",
"expires": "2024-10-18T06:14:15.000Z",
"httpOnly": false,
"secure": false
},
{
"name": "_ga_L943NTQ6FJ",
"value": "GS1.2.1697440574.2.0.1697440574.60.0.0",
"path": "/",
"domain": ".ceshiren.com",
"expires": "2024-11-19T07:16:14.386Z",
"httpOnly": false,
"secure": false
},
{
"name": "JSESSIONID",
"value": "91f733ad-8c18-4142-86cd-08b08dd81c25",
"path": "/",
"domain": "litemall.hogwarts.ceshiren.com",
"expires": "1969-12-31T23:59:59.000Z",
"httpOnly": true,
"secure": false,
"sameSite": "Lax"
},
{
"name": "_gid",
"value": "GA1.2.348187596.1697553224",
"path": "/",
"domain": ".ceshiren.com",
"expires": "2023-10-20T06:14:16.000Z",
"httpOnly": false,
"secure": false
},
{
"name": "Hm_lpvt_214f62eef822bde113f63fedcab70931",
"value": "1697696055",
"path": "/",
"domain": ".ceshiren.com",
"expires": "1969-12-31T23:59:59.000Z",
"httpOnly": false,
"secure": false
},
{
"name": "_ga_XP6GNLH30P",
"value": "GS1.2.1697696060.83.1.1697698552.0.0.0",
"path": "/",
"domain": ".ceshiren.com",
"expires": "2024-11-22T06:55:52.574Z",
"httpOnly": false,
"secure": false
}
],
"headersSize": -1,
"bodySize": 43,
"postData": {
"mimeType": "application/json",
"text": "{\"username\":\"user123\",\"password\":\"user123\"}"
}
},
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "http/2.0",
"headers": [
{
"name": "access-control-allow-credentials",
"value": "true"
},
{
"name": "access-control-allow-origin",
"value": "https://litemall.hogwarts.ceshiren.com"
},
{
"name": "content-encoding",
"value": "gzip"
},
{
"name": "content-type",
"value": "application/json;charset=UTF-8"
},
{
"name": "date",
"value": "Thu, 19 Oct 2023 07:29:59 GMT"
},
{
"name": "server",
"value": "CBS"
},
{
"name": "vary",
"value": "accept-encoding,origin,access-control-request-headers,access-control-request-method,accept-encoding"
}
],
"cookies": [],
"content": {
"size": 436,
"mimeType": "application/json",
"text": "{\"errno\":0,\"data\":{\"userInfo\":{\"nickName\":\"user123\",\"avatarUrl\":\"https://yanxuan.nosdn.127.net/80841d741d7fa3073e0ae27bf487339f.jpg?imageView&quality=90&thumbnail=64x64\"},\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0aGlzIGlzIGxpdGVtYWxsIHRva2VuIiwiYXVkIjoiTUlOSUFQUCIsImlzcyI6IkxJVEVNQUxMIiwiZXhwIjoxNjk3NzA3ODAwLCJ1c2VySWQiOjQwOTk2OSwiaWF0IjoxNjk3NzAwNjAwfQ.hlMFoTvBAdPlDzUzYZHzqdVCOZIF_52468EPEgFEjyg\"},\"errmsg\":\"成功\"}"
},
"redirectURL": "",
"headersSize": -1,
"bodySize": -1,
"_transferSize": 642,
"_error": null
},
"serverIPAddress": "182.92.156.22",
"startedDateTime": "2023-10-19T07:29:59.580Z",
"time": 170.59899994637817,
"timings": {
"blocked": 1.126999993801117,
"dns": -1,
"ssl": -1,
"connect": -1,
"send": 0.477,
"wait": 168.6680000237748,
"receive": 0.3269999288022518,
"_blocked_queueing": 0.5189999938011169
}
}
]
}
}
test_login.py
import requests
def test_case_login():
# 发起请求,json参数根据实际情况自行传入参数,get请求用params传入,post请求如果是formdata用data传入,如果是json就用json传入
response = requests.request(method="POST",url="https://litemall.hogwarts.ceshiren.com/wx/auth/login",json=)
# 断言-状态码
assert response.status_code == 200