干貨 | 通用 api 封裝實(shí)戰(zhàn),帶你深入理解 PO
image1080×434 66.5 KB
在普通的接口自動化測試中,如果接口的參數(shù),比如 url,headers等傳參改變,或者測試用例的邏輯、斷言改變,那么整個測試代碼都需要改變。apiobject設(shè)計模式借鑒了pageobject的設(shè)計模式,可以實(shí)現(xiàn)一個優(yōu)雅、強(qiáng)大的接口測試框架。
理念
apiobject設(shè)計模式可以簡單分為6個模塊,分別是API對象、接口測試框架、配置模塊、數(shù)據(jù)封裝、Utils、測試用例。
接口測試框架:base_api,完成對api的驅(qū)動
API對象:繼承base_api后,完成對接口的封裝
配置模塊:完成配置文件的讀取
數(shù)據(jù)封裝:數(shù)據(jù)構(gòu)造與測試用例的數(shù)據(jù)封裝
Utils:其他功能封裝,改進(jìn)原生框架不足
測試用例:調(diào)用Page/API對象實(shí)現(xiàn)業(yè)務(wù)并斷言
枯燥的講述概念可能難以理解,后面的章節(jié)都會圍繞這些模塊進(jìn)行理論的拆解和實(shí)例的演示。
api 模式應(yīng)用
在這里將會結(jié)合企業(yè)微信的部門管理,獲取部門列表接口作為一個接口測試用例,從沒有封裝到使用apiobject設(shè)計模式進(jìn)行封裝改造。將實(shí)戰(zhàn)與理論結(jié)合,更深入理解apiobject設(shè)計模式。
環(huán)境準(zhǔn)備
企業(yè)微信服務(wù)端API:接口文檔 - 企業(yè)微信開發(fā)者中心
import requests class TestDemo: def test_get_token(self): r = requests.get(url="https://qyapi.weixin.qq.com/cgi-bin/gettoken", params={"corpid": "ww93348658d7c66ef4", "corpsecret": "T0TFrXmGYel167lnkzEydsjl6bcDDeXVmkUnEYugKIw"}) return r.json()["access_token"] def test_department_list(self): r = requests.get(url="https://qyapi.weixin.qq.com/cgi-bin/department/list", params={"access_token": self.test_get_token(), "id": 1}) assert r.json()["errcode"] == 0 return print(r.json())
思路
api
base_api.py是用來封裝所有api的通用方法,比如打印log、對斷言工具做二次封裝等,不牽涉和業(yè)務(wù)相關(guān)的操作
wework.py繼承base_api并實(shí)現(xiàn)基本業(yè)務(wù),之后所有的具體的業(yè)務(wù)資源繼承自wework,比如token的獲取等;
department繼承自wework,用來實(shí)現(xiàn)對應(yīng)模塊具體的業(yè)務(wù)邏輯,比如發(fā)送請求,請求內(nèi)有什么參數(shù)等等。
testcases文件夾內(nèi)統(tǒng)一存放所有的測試用例,調(diào)用API對象實(shí)現(xiàn)業(yè)務(wù)并斷言
utils文件夾內(nèi)存放對其他功能封裝,改進(jìn)原生框架不足
data文件夾數(shù)據(jù)構(gòu)造與測試用例的數(shù)據(jù)封裝此外,還有配置模塊與數(shù)據(jù)封裝會在后面的章節(jié)進(jìn)行具體的介紹
image1080×661 48.8 KB
實(shí)戰(zhàn)案例
utils.py,在此文件中封裝一個jsonpath方法。
import json from jsonpath import jsonpath class Utils: @classmethod def jsonpath(cls, json_object, expr): return jsonpath(json_object, expr)
base_api.py,在此文件中調(diào)用utils中的jsonpath方法。
from test_wework.utils.Utils import Utils class BaseApi: json_data = None def jsonpath(self, expr): return Utils.jsonpath(self.json_data, expr)
wework.py,繼承類BaseApi,實(shí)現(xiàn)token的獲取。將在后面“通用api封裝”章節(jié)中詳細(xì)講述函數(shù)內(nèi)部的設(shè)計。
class WeWork(BaseApi): corpid = "ww93348658d7c66ef4" contact_secret = "T0TFrXmGYel167lnkzEydsjl6bcDDeXVmkUnEYugKIw" token = dict() token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken" @classmethod def get_token(cls, secret=contact_secret): # 避免重復(fù)請求,提高速度 if secret not in cls.token.keys(): r = cls.get_access_token(secret) cls.token[secret] = r["access_token"] return cls.token[secret] @classmethod def get_access_token(cls, secret): r = requests.get(cls.token_url, params={"corpid": cls.corpid, "corpsecret": secret}) return r.json()
department.py,繼承類WeWork,發(fā)起一個get請求,獲取department的list。
class Department(BaseApi): list_url = "https://qyapi.weixin.qq.com/cgi-bin/department/list" def list(self, id): self.json_data = requests.get(self.list_url, params={"access_token": WeWork.get_contact_token(), "id": id}).json() return self.json_data
test_department.py,斷言返回值中的第一個name是否為“WestWayyt”。
class TestDepartment: department = Department() def test_department_list(self): r = self.department.list(1) assert self.department.jsonpath(expr="$..name")[0] == "WestWayyt"
通用 api 封裝實(shí)戰(zhàn)
在apiobject設(shè)計模式中,需要一個“base_api”作為其他api步驟的父類,把通用功能放在這個父類中,供其他的api直接繼承調(diào)用。這樣做的優(yōu)點(diǎn)在于,減少重復(fù)代碼,提高代碼的復(fù)用性。
上文在演示使用api-object設(shè)計模式對腳本進(jìn)行改造時提到了base_api。不過在上文,僅僅只是封裝了一個utils中的一個簡單方法。并沒有完全體現(xiàn)出base_api的實(shí)際作用。
接下來會通過通用接口協(xié)議的定義與封裝實(shí)戰(zhàn),實(shí)際體會一下base_api的巧妙之處。
base_api.py,在代碼內(nèi),對request進(jìn)行一層封裝,當(dāng)然在這里還看不出來具體的優(yōu)勢:
import requests class BaseApi: def request(self, method, url, **kwargs): self.json_data = requests.request(method=method, url=url, **kwargs) return self.json_data
wework.py,繼承于類BaseApi,可以直接調(diào)用父類中的request方法(不需要導(dǎo)入requests庫),從而發(fā)起一個get請求:
from test_interface.test_wework.api.base_api import BaseApi class WeWork(BaseApi): corpid = "ww93348658d7c66ef4" contact_secret = "T0TFrXmGYel167lnkzEydsjl6bcDDeXVmkUnEYugKIw" token = dict() token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken" def get_access_token(self): r = self.request(method="get", url=self.token_url, params={"corpid": self.corpid, "corpsecret": self.contact_secret}) return r.json()
test_wework.py,繼承于類WeWork,主要目的只是為了檢查上面的get_access_token(self) 是否成功:
from test_interface.test_wework.api.wework import WeWork class TestWeWork(WeWork): def test_get_access_token(self): r = self.get_access_token() assert r["errcode"]==0
在上面的案例中,在base_api.py中對 requests 進(jìn)行了多一層的封裝,這樣子,只要是屬于BaseApi這個類的子類,都可以無需引用而直接調(diào)用 requests 庫。從而發(fā)起各種各樣的請求,實(shí)現(xiàn)了通用接口協(xié)議的定義與封裝。
更多技術(shù)文章: https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=hwyun×tamp=1651808542
API
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。