一、接口響應斷言
(一)接口斷言使用場景
- 問題:
- 如何確保請求可以發(fā)送成功。
- 如何保證符合業(yè)務需求。
- 解決方案:
- 通過獲取響應信息,驗證接口請求是否成功,是否符合業(yè)務需求。
Requests 中的響應結果對象
import requests
from requests import Response
# Response就是一個響應對象
r: Response = requests.get('http://www.example.com')
響應結果類型
| 屬性 | 含義 |
|---|---|
r |
響應 Response 對象(可以使用任意的變量名) |
r.status_code |
HTTP 響應狀態(tài)碼 |
r.headers |
返回一個字典,包含響應頭的所有信息。 |
r.text |
返回響應的內容,是一個字符串。 |
r.url |
編碼之后的請求的 url |
r.content |
返回響應的內容,是一個字節(jié)流。 |
r.raw |
響應的原始內容 |
r.json() |
如果響應的內容是 JSON 格式,可以使用該方法將其解析成 Python 對象。 |
# 導入依賴
import requests
def test_res_assert():
# 定義接口的 url 和 json 格式請求體
url = "https://httpbin.ceshiren.com/get"
# 發(fā)出 GET 請求,r 接收接口響應
r = requests.post(url)
響應狀態(tài)碼斷言
- 基礎斷言:
r.status_code
import requests
def test_req():
r = requests.get("https://httpbin.ceshiren.com/get")
assert r.status_code == 200
二、JSON 響應體斷言
1、什么是 JSON 響應體
- JSON格式的響應體指的是HTTP響應中的消息體(message body),它是以JSON格式編碼的數(shù)據(jù)。
{
"name": "John",
"age": 30,
"city": "New York"
}
2、斷言 JSON 格式響應體使用場景
- 驗證API接口的返回結果是否符合預期。
- 業(yè)務場景上是否符合預期。
-
格式是否符合文檔規(guī)范。
3、斷言 JSON 格式響應體
-
r.json():返回 python 字典。
import requests
def test_res_json():
r = requests.get("https://httpbin.ceshiren.com/get")
assert r.status_code == 200
assert r.json()["url"] == "https://httpbin.ceshiren.com/get"
若碰到復雜斷言應該如何處理?
- 多層嵌套的數(shù)據(jù)提取與斷言: JSONPath
- 整體結構響應斷言: JSONSchema
- 自行編寫解析算法
三、整體結構響應斷言
1、響應信息數(shù)據(jù)極為龐大
2、針對于“大響應數(shù)據(jù)”如何斷言
- 針對主要且少量的業(yè)務字段斷言。
- 其他字段不做數(shù)據(jù)正確性斷言,只做類型與整體結構的校驗。
- 與前面的版本進行 diff,對比差異化的地方。
3、JSONSchema 簡介
- 使用 JSON 格式編寫的
- 可以用來定義校驗 JSON 數(shù)據(jù)的結構
- 可以用來校驗 JSON 數(shù)據(jù)的一致性
- 可以用來校驗 API 接口請求和響應
4、JSONSchema 整體結構響應斷言
- 預先生成對應結構的 Schema。
- 將實際獲取到的響應與生成的 Schema 進行對比。
5、JSONSchema 的生成
- 通過界面工具生成。
- 通過第三方庫生成。
- 通過命令行工具生成。
6、JSONSchema 的生成效果
// # 預期的 JSON 文檔結構
{
"name": "Hogwarts",
"Courses": ["Mock", "Docker"]
}
// jsonschema
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome",
"definitions": {
"Welcome": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"Courses": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["Courses", "name"],
"title": "Welcome"
}
}
}
7、界面工具生成
- 復制 JSON 數(shù)據(jù)
- 粘貼到在線生成工具中
- 自動生成 JSON Schema 數(shù)據(jù)
JSON Schema 在線生成工具:https://app.quicktype.io
8、第三方庫生成(Python)
- 安裝:
pip install genson。 - 調用方法生成對應的 JSONSchema 數(shù)據(jù)結構。
from genson import SchemaBuilder
def generate_jsonschema(obj):
# 實例化jsonschem
builder = SchemaBuilder()
# 傳入被轉換的對象
builder.add_object(obj)
# 轉換成 schema 數(shù)據(jù)
return builder.to_schema()
9、JSONSchema 驗證(Python)
- 安裝:
pip install jsonschema。 - 調用
validate()進行驗證。
def schema_validate(obj, schema):
'''
對比 python 對象與生成的 JSONSchame 的結構是否一致
'''
try:
validate(instance=obj, schema=schema)
return True
except Exception as e:
return False
10、JSONSchema 二次封裝
- 生成JSONSchema
- 驗證JSONSchema
class JSONSchemaUtils:
@classmethod
def generate_schema(cls, obj):
# 實例化jsonschem
builder = SchemaBuilder()
# 傳入被轉換的對象
builder.add_object(obj)
# 轉換成 schema 數(shù)據(jù)
return builder.to_schema()
@classmethod
def schema_validate(cls, obj, schema):
'''
對比 python 對象與生成的 json schame 的結構是否一致
'''
try:
validate(instance=obj, schema=schema)
return True
except Exception as e:
return False
四、數(shù)據(jù)庫操作與斷言
1、接口測試響應驗證
如何在測試過程中驗證接口沒有 Bug?
- 通過接口響應值
- 通過查詢數(shù)據(jù)庫信息輔助驗證
2、接口測試數(shù)據(jù)清理
自動化測試過程中,會產(chǎn)生大量的臟數(shù)據(jù),如何處理?
- 通過 Delete 接口刪除
- 自動化測試使用干凈的測試環(huán)境,每次自動化測試執(zhí)行完成之前或之后做數(shù)據(jù)還原。
3、數(shù)據(jù)庫操作注意事項
直接對數(shù)據(jù)庫做查詢之外的操作是非常危險的行為
- 權限管理嚴格的公司數(shù)據(jù)庫權限給的非常低
- 表結構復雜,隨便刪除數(shù)據(jù)會影響測試,甚至會導致系統(tǒng)出現(xiàn)異常
4、接口自動化測試常用的數(shù)據(jù)庫操作
- 連接與配置
- 查詢數(shù)據(jù)與斷言
Python技術棧:章節(jié)《常用第三方庫pymsql》;Java技術棧:《常用標準庫:數(shù)據(jù)庫操作-JDBC》
實戰(zhàn)目標
- 第一次全流程實戰(zhàn)的斷言通過數(shù)據(jù)庫驗證
數(shù)據(jù)庫信息
- 主機: litemall.hogwarts.ceshiren.com
- 端口: 13306
- 用戶名: test
- 密碼: test123456
注意:只有查詢權限
數(shù)據(jù)庫封裝(Python)
- 封裝數(shù)據(jù)庫配置
- 封裝 sql 查詢操作
- 調用方法執(zhí)行 sql 語句
import pymysql
# 封裝建立連接的對象
def get_conn():
conn = pymysql.connect(
host="litemall.hogwarts.ceshiren.com",
port=13306,
user="test",
password="test123456",
database="litemall",
charset="utf8mb4"
)
return conn
# 執(zhí)行sql語句
def execute_sql(sql):
connect = get_conn()
cursor = connect.cursor()
cursor.execute(sql) # 執(zhí)行SQL
record = cursor.fetchone() # 查詢記錄
return record
if __name__ == '__main__':
# 執(zhí)行sql語句查詢user123這個用戶的購物車有一個名稱為 hogwarts1 的商品
execute_sql("select * from litemall_cart where "
"user_id=1 and deleted=0 and "
"goods_name='hogwarts1'")
查詢數(shù)據(jù)與數(shù)據(jù)庫斷言(Python)
- 查詢數(shù)據(jù),添加查詢條件
- 斷言結果不為 None
# 查詢查詢user123這個用戶的購物車有一個名稱為 hogwarts1 的商品
sql_res = execute_sql("select * from litemall_cart where "
"user_id=1 and deleted=0 and "
"goods_name='hogwarts1'")
assert sql_res
五、接口鑒權的多種情況與解決方案
1、接口鑒權是什么
-
身份認證
image.png
image.png
接口鑒權通用的解決方案
- 認證信息的獲取
- 認證信息的攜帶
@startuml
scale 800
if (登錄成功?) then
#pink:響應錯誤;
detach
endif
#palegreen:響應認證信息;
#palegreen:攜帶認證信息發(fā)起其他請求;
@enduml
后端接口鑒權常用方法
@startmindmap
* 常用方式
** cookie
*** 1\. 攜帶身份信息請求認證
*** 2\. 之后的每次請求都攜帶cookie信息,cookie記錄在請求頭中
** token
*** 1\. 攜帶身份信息請求認證
*** 2\. 之后的每次請求都攜帶token認證信息
*** 3\. 可能記錄在請求頭,可能記錄在url參數(shù)中
** auth
*** 每次請求攜帶用戶的username和password,并對其信息加密
** oauth2(選修)
*** 1\. 攜帶身份信息請求認證
*** 2\. 服務端向指定回調地址回傳code
*** 3\. 通過code獲取token
*** 4\. 之后的請求信息都攜帶token。
*** 典型產(chǎn)品 微信自動化測試
@endmindmap
cookie 鑒權
- cookie 的獲取(根據(jù)接口文檔獲?。?/li>
- 發(fā)送攜帶 cookie 的請求
- 直接通過 cookies 參數(shù)
- 通過
Session()對象
import requests
class TestVerify:
def setup_class(self):
self.proxy = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
def test_cookies_by_write(self):
# 簡單場景,直接寫入cookie
url = "https://httpbin.ceshiren.com/cookies"
requests.get(url, proxies=self.proxy, verify=False, cookies={"hogwarts": "ad"})
def test_cookies(self):
# 獲取session 的實例,需要通過Session()保持會話,
# 即為認證之后,之后所有的實例都會攜帶cookie
# 可以模仿用戶在瀏覽器的操作
req = requests.Session()
# 第一次登陸,植入cookie
set_url = "https://httpbin.ceshiren.com/cookies/set/hogwarts/ad"
req.get(set_url, proxies=self.proxy, verify=False)
# 第二次請求的時候即可攜帶cookie信息
url = "https://httpbin.ceshiren.com/cookies"
req.get(url, proxies=self.proxy, verify=False)
token 鑒權
- token 的獲?。ǜ鶕?jù)接口文檔獲?。?/li>
- 發(fā)送攜帶 token 的請求(根據(jù)接口文檔獲?。?/li>
class TestVerify:
def setup_class(self):
self.proxy = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
def test_token(self):
# 1\. 獲取token
url = "http://litemall.hogwarts.ceshiren.com/admin/auth/login"
user_data = {"username": "admin123", "password": "admin123", "code": ""}
r = requests.post(url, json=user_data, proxies=self.proxy, verify=False, )
self.token = r.json()["data"]["token"]
# 2\. 之后的請求均攜帶token
goods_list_url = "http://litemall.hogwarts.ceshiren.com/admin/goods/list"
goods_data = {"name": "hogwarts", "order": "desc", "sort": "add_time"}
r = requests.get(goods_list_url, params=goods_data,
headers={"X-Litemall-Admin-Token": self.token},
proxies=self.proxy, verify=False)
auth 鑒權(了解即可)
- 在基本 HTTP 身份驗證中,請求包含格式為 的標頭字段Authorization: Basic<credentials style="box-sizing: inherit;"></credentials>
- 其中credentials是 ID 和密碼的Base64編碼,由單個冒號連接:。
[圖片上傳失敗...(image-7c678-1694090124455)]
auth 鑒權-代碼示例
import requests
from requests.auth import HTTPBasicAuth
class TestVerify:
def setup_class(self):
self.proxy = {"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"}
def test_basic_auth(self):
# 表示訪問一個需要BasicAuth認證的路徑
# username=用戶名,password=密碼
# 如果不使用basic auth 則會失敗
r = requests.get("https://httpbin.ceshiren.com/basic-auth/username/password",
proxies=self.proxy, verify=False,
auth=HTTPBasicAuth("username", "password"))



