一、什么是接口
接口涉及到四個實體:(我去飯店點餐)
我是客人 :客戶端
廚師:服務器
服務員:接口
菜單:接口文檔
接口定義了一套信息規則讓兩個系統之間互相不必知道對方的內部,只需通過接口就能進行交互
API應用編程接口:各個應用(程序之間進行高效的溝通與協作)?
?
二、HTTP接口
基于HTTP協議的接口
基本特點
- 交互簡單:請求 -> 響應模式,無狀態
- 結構簡單:請求、響應,都是:行、頭、體
- 內容簡單:除了體,其他均為 ASCII 內容
- 應用廣泛:至于體,支持 JSON、HTML、XML、HTML、圖片、視頻、音樂
- 跨平臺兼容性:電腦、電視、手機、智能家居
有冒號的是頭 頭完了兩個換行是體?
?請求
行:方法(GET POST等)
頭:描述(修飾)體
體:無限可能
響應
行:狀態碼
頭:描述(修飾)體
緩存:CacheControl, Last-Modified
內容:Content-Type,Content-Length
體:無限可能
三、 接口測試工具實操
fiddler 的測試步驟:
1. 配置代理,使 Fiddler 錄制請求
2. 點擊 Replay and Edit,修改參數:構造的測試用例
3. 點擊 Run、Start,發送修改后的請求,得到新的響應
優勢:
- 基于真實的請求,有連貫業務場景
- 進行底層的精細化的操作
- 配套的相關請求,由客戶端完成,降低使用門檻
高顏值的 半自動化的接口測試工具:postman
完全自動化就是自己寫python代碼啦?
四、自動化測試的流程
-
分析文檔(理解需求)
- 核心功能
- 登錄:
- 方法:post
- url:?http://api.fbi.com:9225/rest-v2/login/access_token
- body:
- password
- 創建任務:
- 方法:post
- url:?http://api.fbi.com:9225/rest-v2/todo
- body:
- title(選填)
- is_done(選填)
- 查詢任務列表
- 方法:get
- url:?http://api.fbi.com:9225/rest-v2/todo
- 參數:
- page(選填)
- size(選填)
- 刪除任務:
- 方法:delete
- url:?http://api.fbi.com:9225/rest-v2/todo/{todo_id}
- 參數:
- todo_id
- 登錄:
- 次要功能
- 輔助功能
- 理解:
- 有些接口有安全性要求:需要先登錄再使用
- 任務 id:需要動態獲取(接口關聯)
- 核心功能
2. 設計用例
- 用例標題:驗證登錄賬號可刪除任務
- 前置條件:
- 已登錄(得到了身份信息)
- 有任務(已經創建待刪除的任務)
- 測試步驟:
- 得到任務 ID
- 調用刪除接口(結果 1)
- 調用查詢接口(結果 2)
- 斷言(驗證結果):
- 結果 1 成功
- 結果 2 失敗
3. 數據準備
數據驅動測試的數據?件
4. 編寫腳本
- 1. 純代碼腳本
- 2. 框架的?例
5. 執?步驟
6. 斷?結果
五、接口客戶端 requests
HTTP 協議客戶端事實上的標準:
- 不支持異步
- 不支持 HTTP2
pip install requests pytest jsonptah -i https://mirrors.aliyun.com/pypi/simple/
import requestsresp = requests.get("http://www.baidu.com")print(resp.status_code)
requests.request 參數
表單
import requests# 1. 登錄
resp = requests.request(method='post',url="http://api.fbi.com:9225/rest-v2/login/access_token",data={"email": "bf@qq.com","password": "bf123456"}
)print(resp.status_code) # 人工判斷
if resp.status_code == 200: # 自動判斷print('成功')
else:print('失敗')
?
json
import requests# 1. 登錄
resp = requests.request(method='post',url="http://api.fbi.com:9225/rest-v2/login/access_token",json={"email": "bf@qq.com","password": "bf123456"}
)print(resp.status_code) # 人工判斷if resp.status_code == 200: # 自動判斷print('成功')
else:print('失敗')
?文件
import requests# 1. 登錄
resp = requests.request(method='post',url="http://api.fbi.com:9225/rest-v2/login/access_token",files={"email": open("詳細信息顯示.mp4", "rb"),"password": open("惺惺惜惺惺.mp4", "rb"),}
)print(resp.status_code) # 人工判斷
if resp.status_code == 200: # 自動判斷print('成功')
else:print('失敗')
requests.Response
屬性
狀態碼
響應頭
resp = requests.request(method='get',url="http://www.baidu.com",
)print("行:", resp.status_code, resp.reason)
print("頭:", resp.headers, )
print("頭:", dict(resp.cookies), )
響應體
print(resp.content) # 二進制:傳輸
print(resp.text) # 文本:閱讀
print(resp.json()) # 字典:使用 可能失敗
print(resp.json()['token_type'])
?
requests.Session概念
cookies /session
(有多個請求構成的)同一個對話過程
共享參數
請求頭(身份憑據)
共享網絡
HTTP 協議基于 TCP,握手、揮手
最大端口 65535,其中 1024 保留
共享 cookies
🌰 示例場景:模擬用戶登錄知乎
假設我們使用requests.Session
模擬用戶登錄知乎,并在登錄后訪問個人主頁:
- 第一次請求:訪問登錄頁面(獲取初始 Cookies)
- 第二次請求:提交登錄表單(服務器驗證并設置身份 Cookies)
- 第三次請求:訪問個人主頁(自動攜帶登錄態 Cookies)
?
🚀 代碼演示與解析
import requests# 創建Session對象(核心:自動管理Cookies)
session = requests.Session()# 1. 第一次請求:訪問登錄頁面(獲取初始Cookies)
response1 = session.get('https://www.zhihu.com/signin')
print(f"第一次請求后Session中的Cookies: {session.cookies.get_dict()}")
# 輸出示例: {'_zap': 'xxx', 'd_c0': 'yyy'}
第一次請求詳細過程:
- 客戶端發送請求:
GET https://www.zhihu.com/signin
(無 Cookies) - 服務器響應:
- 返回登錄頁面 HTML
- 設置初始 Cookies(如
_zap=xxx; d_c0=yyy
)
Session
自動捕獲并存儲這些 Cookies- 此時
session.cookies
包含:{'_zap': 'xxx', 'd_c0': 'yyy'}
- 此時
# 2. 第二次請求:提交登錄表單(攜帶初始Cookies,獲取身份Cookies)
response2 = session.post(url='https://www.zhihu.com/api/v3/oauth/sign_in',json={'username': 'test@example.com', 'password': '123456'}
)
print(f"第二次請求后Session中的Cookies: {session.cookies.get_dict()}")
# 輸出示例: {'_zap': 'xxx', 'd_c0': 'yyy', 'z_c0': 'token123'}
第二次請求詳細過程:
- 客戶端發送請求:
- 請求頭自動攜帶第一次獲取的 Cookies:
Cookie: _zap=xxx; d_c0=yyy
- 請求體:
{"username": "test@example.com", "password": "123456"}
- 請求頭自動攜帶第一次獲取的 Cookies:
- 服務器響應:
- 驗證登錄信息
- 設置身份 Cookies(如
z_c0=token123
,表示用戶已登錄)
Session
更新 Cookies:- 合并新 Cookies:
{'_zap': 'xxx', 'd_c0': 'yyy', 'z_c0': 'token123'}
- 合并新 Cookies:
# 3. 第三次請求:訪問個人主頁(自動攜帶所有Cookies)
response3 = session.get('https://www.zhihu.com/settings/profile')
print(f"第三次請求后Session中的Cookies: {session.cookies.get_dict()}")
# 輸出示例: {'_zap': 'xxx', 'd_c0': 'yyy', 'z_c0': 'token123'}
第三次請求詳細過程:
- 客戶端發送請求:
- 請求頭自動攜帶所有 Cookies:
Cookie: _zap=xxx; d_c0=yyy; z_c0=token123
- 請求頭自動攜帶所有 Cookies:
- 服務器響應:
- 驗證
z_c0
身份令牌 - 返回個人主頁內容(只有登錄用戶可見)
- 驗證
?
📊 三次請求對比表
請求步驟 | 請求 URL | 請求攜帶的 Cookies | 服務器返回的 Cookies | 最終 Session 中 Cookies |
---|---|---|---|---|
第一次 | /signin | 無 | _zap=xxx; d_c0=yyy | {'_zap': 'xxx', 'd_c0': 'yyy'} |
第二次 | /api/v3/oauth/sign_in | _zap=xxx; d_c0=yyy | z_c0=token123 | {'_zap': 'xxx', 'd_c0': 'yyy', 'z_c0': 'token123'} |
第三次 | /settings/profile | _zap=xxx; d_c0=yyy; z_c0=token123 | 無(或更新已有 Cookies) | {'_zap': 'xxx', 'd_c0': 'yyy', 'z_c0': 'token123'} |
?
🔑 關鍵結論
- Cookies 自動傳遞:
Session
會自動在后續請求中攜帶之前獲取的所有 Cookies,無需手動干預。 - Cookies 自動合并:如果服務器返回新的 Cookies(如登錄后的身份令牌),
Session
會自動合并(覆蓋同名 Cookies,保留不同名的)。 - 會話一致性:整個過程模擬了真實瀏覽器的行為,確保多個請求屬于同一個用戶會話。
?
🛠? 調試技巧
如果你想查看每次請求的詳細信息(包括請求頭、響應頭等),可以添加以下代碼:
# 打印請求信息(包含Cookies)
print(f"請求URL: {response1.request.url}")
print(f"請求頭: {response1.request.headers}")
print(f"響應狀態碼: {response1.status_code}")
print(f"響應頭: {response1.headers}")
六、關聯請求?
接口關聯:使用另一個接口的響應內容,作為下一個接口的請求參數
- 大部分接口都需要:共享參數
- 個別接口才需要:全局變量
其他的提取方式:re、jsonpath
jsonpath 用法:
- 針對字典
- 使用 jsonpath 語法
- 返回列表
import jsonpathtoken = jsonpath.jsonpath(resp.json(), "$.access_token")[0]s.headers.update({"Authorization": f"bearer {token}"})
?
jsonpath
?講解 🧐
jsonpath
?是用于從 JSON 數據(Python 中常表現為字典、列表嵌套結構 )里提取特定內容的工具,類似字符串處理里的正則表達式,但專為 JSON 結構設計,在接口關聯(從接口響應提取數據給下一個接口用)場景超實用,核心要點如下:
?
1. 基礎作用 🎯
從復雜 JSON 結構里,按路徑規則精準提取值。比如接口返回如下 JSON 數據(登錄接口返回帶?access_token
?):
{ "code": 200, "data": { "access_token": "abc123", "expires_in": 3600 }, "msg": "success"
}
想提取?access_token
,就可以用?jsonpath
?按路徑?$.data.access_token
?快速拿到。
?
2. 語法規則(常用) 📖
語法符號 | 含義 | 示例 & 效果 |
---|---|---|
$ | 根節點(整個 JSON 數據的入口) | $.data ?→ 提取?data ?字典內容:{"access_token": "abc123", "expires_in": 3600} |
. | 取子節點、對象屬性 | $.data.access_token ?→ 提取?access_token ?值:abc123 |
[] | 取列表元素、過濾對象(索引 / 條件) | - 列表取值:$.data.users[0] (假設?users ?是列表,取第 1 個元素)- 過濾: $.data.users[?(@.age>18)] (取?users ?里?age ?大于 18 的對象) |
?
3. 在代碼里的使用步驟(結合接口關聯場景) 💻
以登錄后拿?token
?給后續接口用為例:
import requests
import jsonpath # 1. 發送登錄接口請求,拿到響應
login_url = "http://api.example.com/login"
login_data = {"username": "test", "password": "123"}
resp = requests.post(login_url, json=login_data) # 2. 用 jsonpath 提取 access_token(假設響應是 JSON 格式)
# resp.json() 把響應轉成 Python 字典/列表,jsonpath 按規則提取
token_list = jsonpath.jsonpath(resp.json(), "$.data.access_token")
# jsonpath 返回列表,提取第 1 個元素(實際場景需判斷列表非空)
token = token_list[0] if token_list else None # 3. 把 token 放到請求頭,給后續接口用(比如帶 Authorization)
s = requests.Session()
s.headers.update({"Authorization": f"bearer {token}"}) # 4. 后續接口請求自動帶 token(實現接口關聯)
user_info_url = "http://api.example.com/userinfo"
user_resp = s.get(user_info_url)
?
4. 常見場景 & 優勢 🌟
- 接口關聯必備:登錄接口返回的?
token
、創建訂單返回的?order_id
?等,用?jsonpath
?能輕松提取,傳給下一個接口當參數。 - 處理復雜結構:如果 JSON 嵌套很深(比如多層字典、列表混合),用?
.
?逐層找屬性,比 Python 手動寫循環 / 字典取值簡潔太多。 - 兼容性:不管是?
requests
?響應轉的字典,還是普通 JSON 格式字符串轉的字典,都能處理,只要結構符合 JSON 規范。
?
5. 注意事項 ??
jsonpath.jsonpath()
?返回列表,如果沒找到匹配內容,返回?False
?而不是空列表!所以實際用的時候,建議判斷一下:-
result = jsonpath.jsonpath(data, "$.xxx") if result: value = result[0] else: # 處理提取失敗邏輯,比如拋異常、設默認值 raise ValueError("提取 xxx 失敗")
- 語法別和 Python 字典取值搞混,
jsonpath
?用?$.data.access_token
?,Python 原生是?data["access_token"]
?,但?jsonpath
?更適合復雜結構遍歷。
簡單說,jsonpath
?就是為 JSON 提取而生的 “導航儀”,按路徑找數據超方便,接口自動化里處理響應、做關聯請求必學~ 結合代碼多試幾個嵌套結構,就能快速掌握啦 ?
?
?七、完整的自動化實戰?
import requests
import jsonpath# 0. 創建會話,共享參數和網絡鏈接
s = requests.Session()
test_user = "bf@qq.com"
test_pass = "bf123456"# 1. 登錄
resp = s.request(method='post',url="http://api.fbi.com:9225/rest-v2/login/access_token",json={"email": test_user,"password": test_pass}
)if resp.status_code == 200: # 自動判斷print('登錄成功')
else:print('登錄失敗')# 共享參數(身份憑據)
token = jsonpath.jsonpath(resp.json(), "$.access_token")[0]
s.headers.update({"Authorization": f"bearer {token}"})# 2. 查詢任務列表
resp = s.request("get","http://api.fbi.com:9225/rest-v2/todo",
)if resp.status_code == 200:print('查詢任務列表成功')total = resp.json()['total']print(f"當前任務總數: {total}")# 3. 創建任務
resp = s.request("post","http://api.fbi.com:9225/rest-v2/todo",json={"title": "新的任務", "is_done": False}
)if resp.status_code == 200:print('創建任務成功')# 提取變量,以便關聯new_id = jsonpath.jsonpath(resp.json(), "$.id")[0]print(f"新創建的任務ID: {new_id}")# 4. 再次查詢任務列表
resp = s.request("get","http://api.fbi.com:9225/rest-v2/todo",
)if resp.status_code == 200:print('再次查詢任務列表成功')new_total = resp.json()['total']print(f"當前任務總數: {new_total}")if new_total == total + 1:print('任務創建驗證成功')# 5. 刪除任務
resp = s.request("delete",f"http://api.fbi.com:9225/rest-v2/todo/{new_id}",
)if resp.status_code == 200:print('刪除任務成功')# 6. 再次查詢任務列表
resp = s.request("get","http://api.fbi.com:9225/rest-v2/todo",
)if resp.status_code == 200:print('最后一次查詢任務列表成功')renew_total = resp.json()['total']print(f"當前任務總數: {renew_total}")if renew_total == new_total - 1:print('任務刪除驗證成功')if renew_total == total:print('任務總數恢復驗證成功')
?
在 API 認證中,Bearer
?是一種常見的身份驗證方案,用于在 HTTP 請求中傳遞令牌(Token),表明請求者的身份。它是 OAuth 2.0 協議的一部分,也是現代 API 最常用的認證方式之一。
?
📜?Bearer Token 的基本概念
Bearer
?本質是一個授權類型(Authorization Type),告訴服務器:“我持有這個令牌,請驗證我的身份”。它的格式通常是:
Authorization: Bearer <token>
Bearer
:固定關鍵字,表示使用令牌認證。<token>
:服務器頒發的唯一身份令牌(如 JWT、Access Token)。
?
🔐?為什么需要 Bearer?
在傳統的認證方式(如 Basic Auth)中,用戶憑證(用戶名 + 密碼)會直接暴露在請求中,存在安全風險。而?Bearer Token
?允許用戶通過無狀態、一次性的令牌訪問受保護資源,避免了直接傳遞敏感信息。
常見場景:
- 用戶登錄后,服務器返回一個 Token(如 JWT)。
- 客戶端在后續請求中攜帶這個 Token,無需再次登錄。
- 服務器驗證 Token 有效性,確認請求者身份。
?
🚀?代碼中的 Bearer 實現
Bearer
?用于傳遞登錄后獲取的?access_token
:
# 登錄后提取Token
token = jsonpath.jsonpath(resp.json(), "$.access_token")[0]# 將Token添加到請求頭,格式為 "Bearer <token>"
s.headers.update({"Authorization": f"bearer {token}"})
- 注意大小寫:標準寫法是?
Bearer
(首字母大寫),但有些服務器可能不區分大小寫。
?
🔒?安全注意事項
-
Token 泄露風險:Bearer Token 相當于 “數字鑰匙”,一旦泄露,攻擊者可冒充用戶訪問系統。因此:
- 永遠不要在 URL、日志或客戶端代碼中明文存儲 Token。
- 優先使用 HTTPS 確保傳輸安全。
- 為 Token 設置合理的過期時間(如 2 小時)。
-
防止 CSRF 攻擊:Bearer Token 通常用于 API 請求,需配合同源策略(Same-Origin Policy)或 CSRF 令牌保護。
?
🤔?Bearer vs. 其他認證方式
認證方式 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
Bearer Token | 無狀態、易于擴展、支持跨域 | Token 泄露風險高 | REST API、前后端分離 |
Basic Auth | 簡單、標準 | 明文傳輸密碼,安全性低 | 內部系統、臨時訪問 |
OAuth 2.0 | 第三方授權、細粒度權限控制 | 實現復雜 | 開放平臺(如微信登錄) |
?
🌟?總結
Bearer
?是一種通過令牌進行身份驗證的機制,在現代 API 中廣泛使用。它的核心是將服務器頒發的 Token 放在請求頭中,格式為?Authorization: Bearer <token>
,從而實現安全、無狀態的身份驗證。
?
total = resp.json()['total']
?是從 API 響應中提取數據的關鍵步驟!我來拆解一下它的含義和作用~ 🧐
?
1.?代碼拆解:分步解釋
①?resp.json()
resp
?是?requests
?發送請求后得到的響應對象。resp.json()
?是將響應內容(通常是 JSON 格式的字符串)解析為?Python 字典或列表。
例如,如果響應是?{"code": 200, "total": 10}
,則?resp.json()
?返回?{'code': 200, 'total': 10}
。
②?['total']
- 從解析后的字典中,通過鍵(Key)
'total'
?提取對應的值。
例如,上面的例子中?resp.json()['total']
?就是?10
。
?
2.?結合場景理解
這行代碼出現在?查詢任務列表?的步驟中:
# 查詢任務列表
resp = s.request("get", "http://api.fbi.com:9225/rest-v2/todo")
if resp.status_code == 200:total = resp.json()['total'] # 提取任務總數
假設 API 返回的 JSON 是這樣的:
{"code": 200,"message": "success","data": {"items": [{"id": 1, "title": "任務1"},{"id": 2, "title": "任務2"}],"total": 2 # 這是任務總數}
}
?
那么?resp.json()['total']
?就會提取出?2
,表示當前有 2 個任務。
?
3.?為什么需要這個值?
在自動化測試中,這個值通常用于?驗證業務邏輯:
- 比如創建新任務后,檢查總數是否增加 1。
- 刪除任務后,檢查總數是否減少 1。
# 創建任務前的總數
total = resp.json()['total'] # 創建任務...# 創建后的總數
new_total = resp.json()['total'] # 驗證總數是否增加1
if new_total == total + 1:print('任務創建驗證成功')
4.?潛在風險與優化
①?Key 不存在的情況
如果 API 返回的 JSON 中沒有?total
?鍵,直接訪問會報錯。
優化建議:使用?.get()
?方法,不存在時返回默認值(如?None
)。
total = resp.json().get('total') # 不存在時返回None
if total is not None:# 處理邏輯
else:print("響應中缺少'total'字段")
②?響應格式變化
如果 API 升級后返回格式變了(如?total
?移到?data.total
),代碼會失效。
優化建議:使用?jsonpath
?更靈活地提取數據(已經在用啦!)。
total = jsonpath.jsonpath(resp.json(), '$.total')[0] # 更健壯的寫法
🌟?總結
這行代碼的核心作用是:從 API 響應中提取?任務總數,用于后續的業務邏輯驗證(如創建 / 刪除任務后檢查數量變化)。它是接口自動化測試中?數據斷言?的關鍵步驟!