Python接口測試實戰5(下) - RESTful、Web Service及Mock Server
如有任何學習問題,可以添加作者微信:lockingfree
課程目錄
Python接口測試實戰1(上)- 接口測試理論
Python接口測試實戰1(下)- 接口測試工具的使用
Python接口測試實戰2 - 使用Python發送請求
Python接口測試實戰3(上)- Python操作數據庫
Python接口測試實戰3(下)- unittest測試框架
Python接口測試實戰4(上) - 接口測試框架實戰
Python接口測試實戰4(下) - 框架完善:用例基類,用例標簽,重新運行上次失敗用例
Python接口測試實戰5(上) - Git及Jenkins持續集成
Python接口測試實戰5(下) - RESTful、Web Service及Mock Server
更多學習資料請加QQ群: 822601020獲取
本節內容
- REST及RESTful API
- Web Service
- XML解析
- Mock Server
REST及RESTful API
參考鏈接: https://blog.csdn.net/lch2848508/article/details/72729658
REST:表述性狀態轉移或表現層狀態轉移,“表現”及每個接口地址(URI)都表現為(視為)一個資源對象(文本資源、圖片資源、服務資源),狀態轉移指通過POST/PUT方法發送完整的新狀態信息來更改資源對象的狀態
如某https://api.***.com/user資源狀態為{"name": "Kaka", "age": 30}
{"name": "Kaka", "age": 18}
來更新對象信息,完成狀態轉移
URI 與URL的區別:URL值包含協議的鏈接,如
https://www.baidu.com
, 還有一種相對鏈接叫URN,如/doc/1.html
,這兩種都能唯一定位一個資源,URI(統一資源定位符)包含URL和URN
RESTful API是一種接口設計風格或規範,主要有以下特點:
- 統一使用https協議
- 接口使用專用的api域名
https://api.example.com/
- 接口分版本管理
https://api.example.com/v1/
- 路徑又稱"終點"(endpoint),表示API的具體網址,路徑中只能包含名詞(代表資源對象),可以使用復數來代表多個一個資源集合
https://api.example.com/v1/zoos
- 同一個接口提供多種請求方法,GET獲取資源信息,POST新建或更新資源,PUT/PATCH更新資源,DELETE刪除資源
- 可以通過url參數過濾信息
https://api.example.com/v1/zoos?limit=10 # 獲取前10個
- 盡量使用JSON, 避免使用XML
- 身份認證推薦使用OAuth2.0,或Basic Auth,token等,避免使用Cookie和Session(RESTful強調無狀態的設計)
示例:
https://api.github.com
授權 Basic Auth (superhin001, ***) 或 Oauth 2.0 Access Token: 1c4f679300f29ee4e7041028d49e504b9da145b1
GET https://api.github.com/user 獲取用戶信息
POST/PATCH https://api.github.com/user 修改用戶信息
POST/PATCH 數據
{
"login": "superhin001",
"id": 21163682,
"node_id": "MDQ6VXNlcjIxMTYzNjgy",
"avatar_url": "https://avatars3.githubusercontent.com/u/21163682?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/superhin001",
"html_url": "https://github.com/superhin001",
"followers_url": "https://api.github.com/users/superhin001/followers",
"following_url": "https://api.github.com/users/superhin001/following{/other_user}",
"gists_url": "https://api.github.com/users/superhin001/gists{/gist_id}",
"starred_url": "https://api.github.com/users/superhin001/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/superhin001/subscriptions",
"organizations_url": "https://api.github.com/users/superhin001/orgs",
"repos_url": "https://api.github.com/users/superhin001/repos",
"events_url": "https://api.github.com/users/superhin001/events{/privacy}",
"received_events_url": "https://api.github.com/users/superhin001/received_events",
"type": "User",
"site_admin": false,
"name": "我是韓老師",
"company": null,
"blog": "",
"location": null,
"email": "[email protected]",
"hireable": null,
"bio": null,
"public_repos": 3,
"public_gists": 0,
"followers": 0,
"following": 0,
"created_at": "2016-08-22T01:12:32Z",
"updated_at": "2018-09-14T02:33:43Z",
"private_gists": 0,
"total_private_repos": 0,
"owned_private_repos": 0,
"disk_usage": 45430,
"collaborators": 0,
"two_factor_authentication": false,
"plan": {
"name": "free",
"space": 976562499,
"collaborators": 0,
"private_repos": 0
}
}
GET https://api.github.com/user/keys 獲取用戶所有SSH-Key信息
POST https://api.github.com/user/keys 新建Key
POST 數據
{
"id": 30742411,
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfsTJs7mNWstJ+tO6O1jQHKdDdnldqlqkO0gAune9EH7oqICD1hP7c1duNZwvNnvyGa7SyqamIpNXmSYv303FEVAXzPsb9MzCChG16gzevQtbIX4Qt7vFOsHNSCikSCD/s6DMa0Koryiu7Yju5mW9UUnjVM+a1P80SOiK7p2UBQPFVKRrUtr0htV3U6a2rdP51Vzm2UCjChTUa4q7L3m4C7oB9aSvUsNTk+PmuJlAer4oOd7FsNPqD1Or3lRKAmgxbTX4xTaOkwibK0t2eYkh/VTUPMQ9wDwpa4hZLiEq9qSew3McCwsl70k4H0H4F/VwV2sSCXqZu274YmNDT5Hl3 hanzhichao@hanzhichao01",
"title": "test3",
"verified": true,
"created_at": "2018-09-14T09:54:51Z",
"read_only": false
}
Web Service
Web Service 是一種跨平臺(Java對象,Python也可以調用)RPC(遠程方法調用)解決方案。
基於SOAP協議,使用XML這種跨平臺語言傳輸對遠程方法的調用信息及返回結果,並提供WSDL接口描述服務
- SOAP:簡單面向對象協議, 基於XML語言,使用HTTP協議傳輸
- XML: 可擴展標記語言,同JSON一樣是一種跨平臺語言
- WSDL: Web Service服務描述語言,提供遠程對象的調用描述信息(類似於接口文檔,XML格式)
SOAP格式
SOAP協議基於XML語言, SOAP消息體首先必須有個信封(Enelope),信封中可以有信息頭(Header)和信息體(Body),其中Body中還可以包含錯誤信息(Fault)
基本格式如下:
<!-- 信封固定格式 指定命名空間為soapenv -->
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2001/12/soap-envelope" soapenv:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
<soapenv:Header> <!--信息頭 可選,可寫成但標簽-->
......
</soapenv:Header>
<soapenv:Body> <!--信息體 實際調用內容-->
......
</soapenv:Body>
</soap:Envelope>
SOAP詳細教程:http://www.runoob.com/soap/soap-header.html
使用SoupUI
示例接口: http://115.28.108.130:4000/?wsdl
由於Postman等不具備將wsdl接口信息解析成對象描述的功能,我們使用另一個SOAP接口專用的測試工具SoupUI
SoupUI下載地址: http://www.wmzhe.com/soft-32815.html
- 新建項目
- 發送接口
使用Fiddler抓包,查看raw格式:
POST http://115.28.108.130:4000/ HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "addUser"
Content-Length: 370
Host: 115.28.108.130:4000
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:user="UserService">
<soapenv:Header/>
<soapenv:Body>
<user:addUser>
<!--Optional:-->
<user:name>範冰冰</user:name>
<!--Optional:-->
<user:password>123456</user:password>
</user:addUser>
</soapenv:Body>
</soapenv:Envelope>
使用Postman發送SOAP接口
text/xml 和 application/xml的區別: 當你指定編碼的時候,使用application/xml會安裝xml指定的編碼傳輸,而使用text/html會默認使用us-ascii編碼編碼傳輸數據
使用Python操作Web service接口
pip install suds-jurko
from suds.client import Client
service = Client("http://115.28.108.130:4000/?wsdl").service # 獲取遠端服務對象
result = service.addUser("範冰冰", "123456") # 向本地方法一樣調用
print(result) # 輸出 用戶已存在
使用requests庫發送
import requests
url = ‘http://115.28.108.130:4000/‘
data = ‘‘‘<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:user="UserService">
<soapenv:Header/>
<soapenv:Body>
<user:addUser>
<!--Optional:-->
<user:name>張三</user:name>
<!--Optional:-->
<user:password>123456</user:password>
</user:addUser>
</soapenv:Body>
</soapenv:Envelope>
‘‘‘.encode(‘utf-8‘)
res = requests.post(url=url,data=data)
print(res.text)
結果:
<?xml version=‘1.0‘ encoding=‘UTF-8‘?>
<soap11env:Envelope xmlns:soap11env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="UserService"><soap11env:Body><tns:addUserResponse><tns:addUserResult>用戶已存在</tns:addUserResult></tns:addUserResponse></soap11env:Body></soap11env:Envelope>
XML解析
XML: 可擴展標記語言,使用標簽,多級樹狀結構,多用來存儲和傳輸數據,如:
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
- 根元素(根節點),父元素,子元素,同級元素:bookstore為根元素,book是title的父元素,title是book的子元素,title和author是同級元素
- 標簽,屬性,文本:bookstore boot title等為標簽(元素節點),boot中category="COOKING" category為屬性,COOKING為屬性值,之間的Everyday Italian為文本
Python解析XML
- 加載元素樹(ElementTree)得到根節點
- 從根節點使用xpath查找其他節點
from xml.etree import ElementTree
d = ‘‘‘<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
‘‘‘
root = ElementTree.fromstring(d) # 加載元素樹(ElementTree)得到根節點
print(root.find(".")) # 選擇當前節點
print(root.find("book")) # 選擇標簽為book的子節點
print(root.find("book[2]")) # 選擇標簽為book的第三個子節點
print(root.find("book[@category=‘COOKING‘]")) # 選擇標簽為book切標簽屬性為category="COOKING"
print(root.find("./book/[title=‘Harry Potter‘]")) # 選擇標簽為book的節點中包含子標簽title 切title的文本內容為Harry Potter
結果:
<Element ‘bookstore‘ at 0x000002406B666688>
<Element ‘book‘ at 0x000002406B6666D8>
<Element ‘book‘ at 0x000002406B8600E8>
<Element ‘book‘ at 0x000002406B6666D8>
<Element ‘book‘ at 0x000002406B8600E8>
find()返回的是節點對象,可以通過.tag獲取標簽名,.attrib獲取屬性字典,.text獲取文本
XPath選擇器
- 路徑: 按路徑選擇
- 標簽名: 選定所有該名的標簽
- *: 選定所有元素
- . : 選擇當前節點
- ..: 選擇上級節點
- //: 選擇當前元素下所有級別的元素
- [@屬性名]:選擇所有具有當前屬性的元素
- [@屬性名=屬性值]: 選擇屬性=屬性值的元素
- [子標簽名]:選擇具有當前子標簽的元素
- [子標簽名=‘文本‘]: 選擇子標簽文本的元素
常用的三種定位元素方法
- 從根節點按路徑選擇: root.find("./book[3]/title")
- 按屬性並結合相對路徑選擇: root.find(book[@category=‘WEB‘]/title)
- 按所包含的獨特子標簽選擇: root.find(book[title=‘Harry Potter‘]/title)
Mock Server
Mock 即模擬,就是在測試過程中,對於某些不容易構造或者不容易獲取的對象,用一個虛擬的對象來創建以便測試的測試方法,其最大的優勢就是降級前後端耦合度,使前端工程師可以不依賴後端返回數據,先開發前端樣式以及邏輯處理
簡單來說: Mock是用了解決依賴問題的,將復雜的/不穩定的/還未建立的依賴對象用一個簡單的假對象來代替
Mock Server 即Mock接口服務器,可以通過配置快速Mock出新的接口
Mock Server的使用範圍
- 前後端分離項目
- 所測接口依賴第三方系統(還未具備)
- 所測接口依賴復雜或依賴的接口不穩定,並不作為主要驗證對象
同時在接口還未開發好時,提供Mock接口(假接口)會比只有接口文檔更直觀,並能有效減少溝通成本和一些文檔理解bug
Postman的Mock Server功能
Postman還可以基於Collection建立Mock Server,這裏不再詳述
Python+Flask自己搭建Mock接口
使用Flask包我們可以快速搭建Mock接口
pip install flask
from flask import Flask, request, jsonify, abort
import random
app = Flask(__name__) # 實例化一個Flask對象
@app.route("/api/user/reg/", methods=["POST"])
def reg():
if not request.json or not ‘name‘ in request.json or not ‘password‘ in request.json:
abort(404)
res = [
{
"code": "100000",
"msg": "成功",
"data": {
"name": "李六",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
},
{
"code": "100001",
"msg": "失敗,用戶已存在",
"data": {
"name": "李六",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
},
{
"code": "100002",
"msg": "失敗,添加用戶失敗",
"data": {
"name": "李六",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
}
]
return jsonify(random.choice(res))
if __name__ == ‘__main__‘:
app.run()
此為北京龍騰育才 Python高級自動化(接口測試部分)授課筆記
課程介紹
想要參加現場(北京)/網絡課程的可以聯系作者微信:lockingfree
- 高效學習,快速掌握Python自動化所有領域技能
- 同步快速解決各種問題
- 配套實戰項目練習
Python接口測試實戰5(下) - RESTful、Web Service及Mock Server