前言
HTTP介面測試很簡單,不管工具、框架、還是平臺,只要很的好的幾個點就是好工具。
- 測試資料問題:比如刪除介面,重複執行還能保持結果一致,必定要做資料初始化。
- 介面依賴問題:B介面依賴A的返回值,C介面依賴B介面的返回值。
- 加密問題:不同的介面加密規則不一樣。有些用到時間戳、md5、base64、AES,如何提供種能力。
- 斷言問題:有些介面返回的結構體很複雜,如何靈活的做到斷言。
對於以上問題,工具和平臺要麼不支援,要麼很麻煩,然而框架是最靈活的。
unittest/pytest + requests/https 直接上手寫程式碼就好了,既簡單又靈活。
那麼同樣是寫程式碼,A框架需要10行,B框架只需要5行,然而又不失靈活性,那我當然是選擇更少的了,畢竟,人生苦短嘛。
seldom適合個人介面自動化專案,它有以下優勢。
- 可以寫更少的程式碼
- 自動生成HTML/XML測試報告
- 支援引數化,減少重複的程式碼
- 支援生成隨機資料
- 支援har檔案轉case
- 支援資料庫操作
這些是seldom支援的功能,我們只需要整合HTTP介面庫,並提供強大的斷言即可。seldom 2.0
加入了HTTP介面自動化測試支援。
Seldom 相容 Requests API 如下:
seldom | requests |
---|---|
self.get() | requests.get() |
self.post() | requests.post() |
self.put() | requests.put() |
self.delete() | requests.delete() |
Seldom VS Request+unittest
先來看看unittest + requests是如何來做介面自動化的:
import unittest
import requests
class TestAPI(unittest.TestCase):
def test_get_method(self):
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.get("http://httpbin.org/get", params=payload)
self.assertEqual(r.status_code, 200)
if __name__ == '__main__':
unittest.main()
這其實已經非常簡潔了。同樣的用例,用seldom實現。
# test_req.py
import seldom
class TestAPI(seldom.TestCase):
def test_get_method(self):
payload = {'key1': 'value1', 'key2': 'value2'}
self.get("http://httpbin.org/get", params=payload)
self.assertStatusCode(200)
if __name__ == '__main__':
seldom.main()
主要簡化點在,介面的返回資料的處理。當然,seldom真正的優勢在斷言、日誌和報告。
har to case
對於不熟悉 Requests 庫的人來說,通過Seldom來寫介面測試用例還是會有一點難度。於是,seldom提供了har
檔案轉 case
的命令。
首先,開啟fiddler 工具進行抓包,選中某一個請求。
然後,選擇選單欄:file
-> Export Sessions
-> Selected Sessions...
選擇匯出的檔案格式。
點選next
儲存為demo.har
檔案。
最後,通過seldom -h2c
轉為demo.py
指令碼檔案。
> seldom -h2c .\demo.har
.\demo.py
2021-06-14 18:05:50 [INFO] Start to generate testcase.
2021-06-14 18:05:50 [INFO] created file: D:\.\demo.py
demo.py
檔案。
import seldom
class TestRequest(seldom.TestCase):
def start(self):
self.url = "http://httpbin.org/post"
def test_case(self):
headers = {"User-Agent": "python-requests/2.25.0", "Accept-Encoding": "gzip, deflate", "Accept": "application/json", "Connection": "keep-alive", "Host": "httpbin.org", "Content-Length": "36", "Origin": "http://httpbin.org", "Content-Type": "application/json", "Cookie": "lang=zh"}
cookies = {"lang": "zh"}
self.post(self.url, json={"key1": "value1", "key2": "value2"}, headers=headers, cookies=cookies)
self.assertStatusCode(200)
if __name__ == '__main__':
seldom.main()
執行測試
開啟debug模式seldom.run(debug=True)
執行上面的用例。
> python .\test_req.py
2021-04-29 18:19:39 [INFO] A run the test in debug mode without generating HTML report!
2021-04-29 18:19:39 [INFO]
__ __
________ / /___/ /___ ____ ____
/ ___/ _ \/ / __ / __ \/ __ ` ___/
(__ ) __/ / /_/ / /_/ / / / / / /
/____/\___/_/\__,_/\____/_/ /_/ /_/
-----------------------------------------
@itest.info
test_get_method (test_req.TestAPI) ...
----------- Request ? ---------------
url: http://httpbin.org/get method: GET
----------- Response ?️ -------------
type: json
{'args': {'key1': 'value1', 'key2': 'value2'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0', 'X-Amzn-Trace-Id': 'Root=1-608a883c-7b355ba81fcd0d287566405a'}, 'origin': '183.178.27.36', 'url': 'http://httpbin.org/get?key1=value1&key2=value2'}
ok
----------------------------------------------------------------------
Ran 1 test in 0.619s
OK
通過日誌/報告都可以清楚的看到。
- 請求的方法
- 請求url
- 響應的型別
- 響應的資料
更強大的斷言
斷言介面返回的資料是我們在做介面自動化很重要的工作。
assertJSON
介面返回結果如下:
{
"args": {
"hobby": [
"basketball",
"swim"
],
"name": "tom"
}
}
我的目標是斷言name
和 hobby
部分的內容。seldom可以針對JSON
檔案進行斷言。
import seldom
class TestAPI(seldom.TestCase):
def test_assert_json(self):
payload = {'name': 'tom', 'hobby': ['basketball', 'swim']}
self.get("http://httpbin.org/get", params=payload)
assert_json = {'args': {'hobby': ['swim', 'basketball'], 'name': 'tom'}}
self.assertJSON(assert_json)
執行日誌
test_get_method (test_req.TestAPI) ...
----------- Request ? ---------------
url: http://httpbin.org/get method: GET
----------- Response ?️ -------------
type: json
{'args': {'hobby': ['basketball', 'swim'], 'name': 'tom'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0', 'X-Amzn-Trace-Id': 'Root=1-608a896d-48fac4f6139912ba01d2626f'}, 'origin': '183.178.27.36', 'url': 'http://httpbin.org/get?name=tom&hobby=basketball&hobby=swim'}
? Assert data has not key: headers
? Assert data has not key: origin
? Assert data has not key: url
ok
----------------------------------------------------------------------
Ran 1 test in 1.305s
OK
seldom還會提示你還有哪些欄位沒有斷言。
assertPath
介面返回資料如下:
{
"args": {
"hobby":
["basketball", "swim"],
"name": "tom"
}
}
seldom中可以通過path進行斷言:
import seldom
class TestAPI(seldom.TestCase):
def test_assert_path(self):
payload = {'name': 'tom', 'hobby': ['basketball', 'swim']}
self.get("http://httpbin.org/get", params=payload)
self.assertPath("name", "tom")
self.assertPath("args.hobby[0]", "basketball")
assertSchema
有時並不關心資料本身是什麼,而是需要斷言資料的型別。 assertSchema
是基於 jsonschema
實現的斷言方法。
jsonschema: https://json-schema.org/learn/
介面返回資料如下:
{
"args": {
"hobby":
["basketball", "swim"],
"name": "tom",
"age": "18"
}
}
seldom中可以通過利用jsonschema
進行斷言:
import seldom
class TestAPI(seldom.TestCase):
def test_assert_schema(self):
payload = {"hobby": ["basketball", "swim"], "name": "tom", "age": "18"}
self.get("/get", params=payload)
schema = {
"type": "object",
"properties": {
"args": {
"type": "object",
"properties": {
"age": {"type": "string"},
"name": {"type": "string"},
"hobby": {
"type": "array",
"items": {
"type": "string"
},
}
}
}
},
}
self.assertSchema(schema)
是否再次感受到了seldom提供的斷言非常靈活,強大。
介面資料依賴
在場景測試中,我們需要利用上一個介面的資料,呼叫下一個介面。
import seldom
class TestRespData(seldom.TestCase):
def test_data_dependency(self):
"""
Test for interface data dependencies
"""
headers = {"X-Account-Fullname": "bugmaster"}
self.get("/get", headers=headers)
self.assertStatusCode(200)
username = self.response["headers"]["X-Account-Fullname"]
self.post("/post", data={'username': username})
self.assertStatusCode(200)
seldom提供了self.response
用於記錄上個介面返回的結果,直接拿來用即可。
資料驅動
seldom本來就提供的有強大的資料驅動,拿來做介面測試非常方便。
@data
import seldom
from seldom import data
class TestDDT(seldom.TestCase):
@data([
("key1", 'value1'),
("key2", 'value2'),
("key3", 'value3')
])
def test_data(self, key, value):
"""
Data-Driver Tests
"""
payload = {key: value}
self.post("/post", data=payload)
self.assertStatusCode(200)
self.assertEqual(self.response["form"][key], value)
@file_data
建立data.json
資料檔案
{
"login": [
["admin", "admin123"],
["guest", "guest123"]
]
}
通過file_data
實現資料驅動。
import seldom
from seldom import file_data
class TestDDT(seldom.TestCase):
@file_data("data.json", key="login")
def test_data(self, username, password):
"""
Data-Driver Tests
"""
payload = {username: password}
self.post("http://httpbin.org/post", data=payload)
self.assertStatusCode(200)
self.assertEqual(self.response["form"][username], password)
更過資料檔案(csv/excel/yaml),參考
隨機生成測試資料
seldom提供隨機生成測試資料方法,可以生成一些常用的資料。
import seldom
from seldom import testdata
class TestAPI(seldom.TestCase):
def test_data(self):
phone = testdata.get_phone()
payload = {'phone': phone}
self.get("http://httpbin.org/get", params=payload)
self.assertPath("args.phone", phone)
更過型別的測試資料,參考
資料庫操作
seldom 支援sqlite3、MySQL資料庫操作。
sqlite3 | MySQL |
---|---|
delete_data() | delete_data() |
insert_data() | insert_data() |
select_data() | select_data() |
update_data() | update_data() |
init_table() | init_table() |
close() | close() |
連線資料庫
連線sqlit3資料庫
from seldom.db_operation import SQLiteDB
db = SQLiteDB(r"D:\learnAPI\db.sqlite3")
連線MySQL資料庫(需要)
- 安裝pymysql驅動
> pip install pymysql
- 連結
from seldom.db_operation import MySQLDB
db = MySQLDB(host="127.0.0.1",
port="3306",
user="root",
password="123",
database="db_name")
操作方法
- delete_data
刪除表資料。
db.delete_data(table="user", where={"id":1})
- insert_data
插入一條資料。
data = {'id': 1, 'username': 'admin', 'password': "123"},
db.insert_data(table="user", data=data)
- select_data
查詢表資料。
result = db.select_data(table="user", where={"id":1, "name": "tom"})
print(result)
- update_data
更新表資料。
db.update_data(table="user", data={"name":"new tom"}, where={"name": "tom"})
- init_table
批量插入資料,在插入之前先清空表資料。
datas = {
'api_event': [
{'id': 1, 'name': '紅米Pro釋出會'},
{'id': 2, 'name': '可參加人數為0'},
{'id': 3, 'name': '當前狀態為0關閉'},
{'id': 4, 'name': '釋出會已結束'},
{'id': 5, 'name': '小米5釋出會'},
],
'api_guest': [
{'id': 1, 'real_name': 'alen'},
{'id': 2, 'real_name': 'has sign'},
{'id': 3, 'real_name': 'tom'},
]
}
db.init_table(datas)
- close
關閉資料庫連線。
db.close()
最後,基於seldom 實現介面自動化測試的專案:https://github.com/defnngj/pyrequest2