安裝
$ pip install httprunner==2.3.0
搭建項目
$ hrun --startproject demo
Start to create new project: demo
CWD: /Users/zhongxin/PycharmProjects/learn_httprunner
created folder: demo
created folder: demo/api
created folder: demo/testcases
created folder: demo/testsuites
created folder: demo/reports
created file: demo/api/demo_api.yml
created file: demo/testcases/demo_testcase.yml
created file: demo/testsuites/demo_testsuite.yml
created file: demo/debugtalk.py
created file: demo/.env
created file: demo/.gitignore
項目結構
demo/api
最小單元,單個接口的操作
demo/testcases
一個測試點,多個接口依次調用
demo/testsuites
測試套,執行多個測試點
demo/reports
測試報告存放路徑
demo/.env
環境配置文件
demo/debugtalk.py
使用python代碼進行一些自定義操作
demo/.gitignore
git 版本管理提交時忽略的文件或文件夾
單個接口實例
先寫一個簡單的接口
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
right_u_p = {
"username": "測試遊記",
"password": "123456"
}
@app.route('/login', methods=['POST'])
def login():
data = json.loads(request.data.decode())
if data.get('username') == right_u_p['username'] and data.get('password') == right_u_p['password']:
return jsonify({"Data": "賬號密碼正確"}), 200
else:
return jsonify({"Data": "賬號密碼錯誤"}), 401
if __name__ == '__main__':
app.run()
在demo/api
中編寫第一個登陸的接口測試login.yml
name: 登陸接口
variables:
var1: value1
var2: value2
request:
url: http://127.0.0.1:5000/login
method: POST
headers:
Content-Type: "application/json"
json:
username: "測試遊記"
password: "123456"
validate:
- eq: ["status_code", 200]
運行
$ hrun /Users/zhongxin/PycharmProjects/learn_httprunner/demo/api/login.yml
運行報告
運行詳情
使用環境變量
修改demo/.env
USERNAME=測試遊記
PASSWORD=123456
name: 登陸接口
variables:
var1: value1
var2: value2
request:
url: http://127.0.0.1:5000/login
method: POST
headers:
Content-Type: "application/json"
json:
username: ${ENV(USERNAME)}
password: ${ENV(PASSWORD)}
validate:
- eq: ["status_code", 200]
定義全局變量
name: 登陸接口
variables:
uname: "測試遊記"
pwd: "123456"
request:
url: http://127.0.0.1:5000/login
method: POST
headers:
Content-Type: "application/json"
json:
username: $uname
password: $pwd
validate:
- eq: ["status_code", 200]
定義方法
在variables
中定義
使用方法
使用$變量名
調用python函數
在demo/debugtalk.py
中編寫獲取隨機user_agent的函數
def get_user_agent():
user_agent = [
"Mozilla/5.0 ce",
"Mozilla/5.0 shi",
"Mozilla/5.0 you",
"Mozilla/5.0 ji",
]
return random.choice(user_agent)
在login.yml
中使用
name: 登陸接口
variables:
uname: "測試遊記"
pwd: "123456"
request:
url: http://127.0.0.1:5000/login
method: POST
headers:
Content-Type: "application/json"
User-Agent: ${get_user_agent()}
json:
username: $uname
password: $pwd
validate:
- eq: ["status_code", 200]
base_url
提取url路徑
name: 登陸接口
variables:
uname: "測試遊記"
pwd: "123456"
base_url: http://127.0.0.1:5000
request:
url: /login
method: POST
headers:
Content-Type: "application/json"
User-Agent: ${get_user_agent()}
json:
username: $uname
password: $pwd
validate:
- eq: ["status_code", 200]
validate
查看httprunner的源碼:./site-packages/httprunner/validator.py
從get_uniform_comparator
可以看出它支持的斷言以及其簡寫方式
def get_uniform_comparator(comparator):
""" convert comparator alias to uniform name
"""
if comparator in ["eq", "equals", "==", "is"]:
return "equals"
elif comparator in ["lt", "less_than"]:
return "less_than"
elif comparator in ["le", "less_than_or_equals"]:
return "less_than_or_equals"
elif comparator in ["gt", "greater_than"]:
return "greater_than"
elif comparator in ["ge", "greater_than_or_equals"]:
return "greater_than_or_equals"
elif comparator in ["ne", "not_equals"]:
return "not_equals"
elif comparator in ["str_eq", "string_equals"]:
return "string_equals"
elif comparator in ["len_eq", "length_equals", "count_eq"]:
return "length_equals"
elif comparator in ["len_gt", "count_gt", "length_greater_than", "count_greater_than"]:
return "length_greater_than"
elif comparator in ["len_ge", "count_ge", "length_greater_than_or_equals", \
"count_greater_than_or_equals"]:
return "length_greater_than_or_equals"
elif comparator in ["len_lt", "count_lt", "length_less_than", "count_less_than"]:
return "length_less_than"
elif comparator in ["len_le", "count_le", "length_less_than_or_equals", \
"count_less_than_or_equals"]:
return "length_less_than_or_equals"
else:
return comparator
查看返回信息中的字段
name: 登陸接口
variables:
uname: "測試遊記"
pwd: "123456"
base_url: http://127.0.0.1:5000
request:
url: /login
method: POST
headers:
Content-Type: "application/json"
User-Agent: ${get_user_agent()}
json:
username: $uname
password: $pwd
validate:
- eq: ["status_code", 200]
- eq: ["content.Data", "賬號密碼正確"]
testcases
爲了測試連續多個步驟
場景:登陸後獲取項目信息
修改接口代碼
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
right_u_p = {
"username": "測試遊記",
"password": "123456"
}
@app.route('/login', methods=['POST'])
def login():
data = json.loads(request.data.decode())
if data.get('username') == right_u_p['username'] and data.get('password') == right_u_p['password']:
return jsonify({"Data": "賬號密碼正確", "token": "123456"}), 200
else:
return jsonify({"Data": "賬號密碼錯誤"}), 401
@app.route('/projects')
def project():
print(request.headers.get("Authorization"))
if request.headers.get("Authorization") == '123456':
return jsonify({"Data": []}), 200
else:
return jsonify({"Data": []}), 401
if __name__ == '__main__':
app.run()
demo/api/get_project_list.yml
name: 獲取項目信息
variables:
token: "xxxxx"
base_url: http://127.0.0.1:5000
request:
url: /projects
method: GET
headers:
Content-Type: "application/json"
Authorization: $token
validate:
- eq: ["status_code", 200]
demo/testcases/get_project.yml
可以直接使用已經編寫好的接口yaml文件當作步驟
config:
name: "獲取項目信息"
variables:
username: ${ENV(USERNAME)}
password: ${ENV(PASSWORD)}
base_url: "http://127.0.0.1:5000"
teststeps:
-
name: 登陸
api: api/login.yml
extract:
- token: content.token
-
name: 獲取項目列表信息
api: api/get_project_list.yml
testsuites
單個接口demo/api/login.yml
name: 登陸接口
variables:
uname: "測試遊記"
pwd: "123456"
base_url: http://127.0.0.1:5000
request:
url: /login
method: POST
headers:
Content-Type: "application/json"
User-Agent: ${get_user_agent()}
json:
username: $uname
password: $pwd
validate:
- eq: ["status_code", 200]
- eq: ["content.Data", "賬號密碼正確"]
參數不固定的接口測試demo/testcases/login.yml
缺少參數:
title 標題
uname 賬號
pwd 密碼
config:
name: "登陸接口配置"
base_url: "http://127.0.0.1:5000"
teststeps:
-
name: $title
api: api/login.yml
validate:
- eq: ["status_code", $status_code]
- eq: ["content.Data", $content_data]
參數遍歷的測試套demo/testsuites/login.yml
使用直接寫入
config:
name: "接口套件"
testcases:
- name: "登陸接口套件"
testcase: testcases/login.yml
parameters:
- title-uname-pwd-status_code-content_data:
- ["正常登陸","測試遊記","123456",200,"賬號密碼正確"]
- ["賬號錯誤","測試遊記1","",401,"賬號密碼錯誤"]
- ["密碼錯誤","測試遊記","1234567",401,"賬號密碼錯誤"]
- ["賬號爲空","","1234567",401,"賬號密碼錯誤"]
- ["密碼爲空","測試遊記","",401,"賬號密碼錯誤"]
使用csv
新建demo/datas/account.csv
title,uname,pwd,status_code,content_data
正常登陸,測試遊記,123456,200,賬號密碼正確
賬號錯誤,測試遊記1,,401,賬號密碼錯誤
密碼錯誤,測試遊記,1234567,401,賬號密碼錯誤
賬號爲空,,1234567,401,賬號密碼錯誤
密碼爲空,測試遊記,,401,賬號密碼錯誤
config:
name: "接口套件"
testcases:
- name: "登陸接口套件"
testcase: testcases/login.yml
parameters:
- title-uname-pwd-status_code-content_data: ${P(datas/account.csv)}
使用csv會出現str和int類型比較的問題,需要在demo/debugtalk.py
編寫代碼解決
命令行執行
from httprunner.api import HttpRunner
httpruner = HttpRunner()
httpruner.run(r'/Users/zhongxin/PycharmProjects/learn_httprunner/demo/testsuites/login.yml')
print(httpruner.summary)