1.Httprunner簡介
官網https://testerhome.com/opensource_projects/httprunner
HttpRunner 是一款面向 HTTP(S) 協議的通用測試框架,只需編寫維護一份YAML/JSON
腳本,即可實現自動化測試、性能測試、線上監控、持續集成等多種測試需求。
- 繼承 Requests 的全部特性,輕鬆實現 HTTP(S) 的各種測試需求
- 測試用例與代碼分離,採用
YAML/JSON
的形式描述測試場景,保障測試用例具備可維護性 - 測試用例支持分層機制,充分實現測試用例的複用
- 測試用例支持參數化和數據驅動機制
- 使用 skip 機制實現對測試用例的分組執行控制
- 支持熱加載機制,在文本測試用例中輕鬆實現複雜的動態計算邏輯
- 基於 HAR 實現接口錄製和用例生成功能(har2case)
- 結合 Locust 框架,無需額外的工作即可實現分佈式性能測試
- 執行方式採用 CLI 調用,可與 Jenkins 等持續集成工具完美結合
- 測試結果統計報告簡潔清晰,附帶詳盡統計信息和日誌記錄
- 具有可擴展性,便於擴展實現 Web 平臺化(HttpRunnerManager)
2.環境搭建
pip install httprunner
3.基本使用介紹
httprunner主要是通過YAML或JSON形式描述各種測試用例的,當你有了YAML或JSON格式的測試用例後,只需執行
hrun xxx.yml,或hrun xxx.json,即可執行測試用例
所以學習httprunner主要是編寫yml或json格式的測試用例
yml或json格式的用例有兩種方法獲取1.自己編寫2.通過抓包,使用har2case工具轉換獲得
先來一個簡單的錄製獲取用例的demo
1.準備api_server.py,這個是一個flask框架編寫的接口文件,需要先安裝flask框架,
import hashlib
import hmac
import json
import random
import string
from functools import wraps
from flask import Flask, make_response, request
SECRET_KEY = "DebugTalk"
app = Flask(__name__)
""" storage all users' data
data structure:
users_dict = {
'uid1': {
'name': 'name1',
'password': 'pwd1'
},
'uid2': {
'name': 'name2',
'password': 'pwd2'
}
}
"""
users_dict = {}
""" storage all token data
data structure:
token_dict = {
'device_sn1': 'token1',
'device_sn2': 'token1'
}
"""
token_dict = {}
def gen_random_string(str_len):
""" generate random string with specified length
"""
return ''.join(
random.choice(string.ascii_letters + string.digits) for _ in range(str_len))
def get_sign(*args):
content = ''.join(args).encode('ascii')
sign_key = SECRET_KEY.encode('ascii')
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
return sign
def gen_md5(*args):
return hashlib.md5("".join(args).encode('utf-8')).hexdigest()
def validate_request(func):
@wraps(func)
def wrapper(*args, **kwargs):
device_sn = request.headers.get('device_sn', "")
token = request.headers.get('token', "")
if not device_sn or not token:
result = {
'success': False,
'msg': "device_sn or token is null."
}
response = make_response(json.dumps(result), 401)
response.headers["Content-Type"] = "application/json"
return response
if token_dict.get(device_sn) != token:
result = {
'success': False,
'msg': "Authorization failed!"
}
response = make_response(json.dumps(result), 403)
response.headers["Content-Type"] = "application/json"
return response
return func(*args, **kwargs)
return wrapper
@app.route('/')
def index():
return "Hello World!"
@app.route('/api/get-token', methods=['POST'])
def get_token():
device_sn = request.headers.get('device_sn', "")
os_platform = request.headers.get('os_platform', "")
app_version = request.headers.get('app_version', "")
data = request.get_json()
sign = data.get('sign', "")
expected_sign = get_sign(device_sn, os_platform, app_version)
if expected_sign != sign:
result = {
'success': False,
'msg': "Authorization failed!"
}
response = make_response(json.dumps(result), 403)
else:
token = gen_random_string(16)
token_dict[device_sn] = token
result = {
'success': True,
'token': token
}
response = make_response(json.dumps(result))
response.headers["Content-Type"] = "application/json"
return response
@app.route('/api/users')
@validate_request
def get_users():
users_list = [user for uid, user in users_dict.items()]
users = {
'success': True,
'count': len(users_list),
'items': users_list
}
response = make_response(json.dumps(users))
response.headers["Content-Type"] = "application/json"
return response
@app.route('/api/reset-all')
@validate_request
def clear_users():
users_dict.clear()
result = {
'success': True
}
response = make_response(json.dumps(result))
response.headers["Content-Type"] = "application/json"
return response
@app.route('/api/users/<int:uid>', methods=['POST'])
@validate_request
def create_user(uid):
user = request.get_json()
if uid not in users_dict:
result = {
'success': True,
'msg': "user created successfully."
}
status_code = 201
users_dict[uid] = user
else:
result = {
'success': False,
'msg': "user already existed."
}
status_code = 500
response = make_response(json.dumps(result), status_code)
response.headers["Content-Type"] = "application/json"
return response
@app.route('/api/users/<int:uid>')
@validate_request
def get_user(uid):
user = users_dict.get(uid, {})
if user:
result = {
'success': True,
'data': user
}
status_code = 200
else:
result = {
'success': False,
'data': user
}
status_code = 404
response = make_response(json.dumps(result), status_code)
response.headers["Content-Type"] = "application/json"
return response
@app.route('/api/users/<int:uid>', methods=['PUT'])
@validate_request
def update_user(uid):
user = users_dict.get(uid, {})
if user:
user = request.get_json()
success = True
status_code = 200
users_dict[uid] = user
else:
success = False
status_code = 404
result = {
'success': success,
'data': user
}
response = make_response(json.dumps(result), status_code)
response.headers["Content-Type"] = "application/json"
return response
@app.route('/api/users/<int:uid>', methods=['DELETE'])
@validate_request
def delete_user(uid):
user = users_dict.pop(uid, {})
if user:
success = True
status_code = 200
else:
success = False
status_code = 404
result = {
'success': success,
'data': user
}
response = make_response(json.dumps(result), status_code)
response.headers["Content-Type"] = "application/json"
return response
if __name__ == '__main__':
app.run()
直接運行後,訪問http://127.0.0.1:5000/
使用fiddler抓包後,
selected Sessions,選擇httpArchive v1.1,導出爲test.har
2.使用har2case將har文件轉換爲yml或json格式
不加 -2y參數默認轉換爲json格式,加了後是yml格式
test.json
{
"teststeps": [
{
"validate": [
{
"eq": [
"status_code",
200
]
},
{
"eq": [
"headers.Content-Type",
"text/html; charset=utf-8"
]
}
],
"request": {
"url": "http://127.0.0.1:5000/",
"headers": {
"User-Agent": "Fiddler"
},
"method": "GET"
},
"name": "/"
}
],
"config": {
"variables": {},
"name": "testcase description"
}
}
test.yml
config:
name: testcase description
variables: {}
teststeps:
- name: !!python/unicode '/'
request:
headers:
!!python/unicode 'User-Agent': !!python/unicode 'Fiddler'
method: !!python/unicode 'GET'
url: !!python/unicode 'http://127.0.0.1:5000/'
validate:
- eq:
- status_code
- 200
- eq:
- headers.Content-Type
- !!python/unicode 'text/html; charset=utf-8'
生成的就是測試用例了,然後使用命令進行運行,
可以查看生成的報告