Tep 支持 MVC 架構實現(xiàn)用例數(shù)據(jù)分離
大家好,我是剛哥。
tep已經(jīng)支持了兩種開發(fā)模式:用例數(shù)據(jù)一體和HttpRunner(第三版)。HttpRunner已經(jīng)眾所周知,此處不做過多介紹。用例數(shù)據(jù)一體指的是把用例和數(shù)據(jù)都寫在一個test.py文件里面,這樣的好處是簡單直接明了,特別適合剛開始用pytest寫自動化代碼的同學,從上往下一氣呵成就完成了一個自動化用例,收獲成就感的同時,也能很方便的共享給其他人使用,單個文件就能跑起來。
但是當我在公司用SpringBoot開發(fā)了一個后端服務后,嘗到了MVC架構所帶來的高可維護性,加上在跟各位大佬聊天時,也聽說很多公司對接口自動化做了分層設計,于是讓tep支持用例數(shù)據(jù)分離的開發(fā),已經(jīng)變得很重要了。用例數(shù)據(jù)分離指的是用例只有邏輯沒有數(shù)據(jù),這樣在修改數(shù)據(jù)的時候,用例基本不需要變化就能適應,用例看起來是非常簡潔的,就像這樣:
- class Test:
- case_vars = TepVars()
- case_vars.vars_ = {
- "domain": "http://127.0.0.1:5000",
- "skuNum": "3"
- }
- def test(self):
- # 登錄
- Login(Test).post()
- # 搜索商品
- SearchSku(Test).get()
- # 添加購物車
- AddCart(Test).post()
- # 下單
- Order(Test).post()
- # 支付
- Pay(Test).post()
這就是tep支持的用例數(shù)據(jù)分離的用例的樣子。
我增加了一個services文件夾,用于存放接口:
先看下登錄Login.py:
- class Login(BaseRequest):
- def post(self):
- response = self.request(
- "post",
- url=self.case_vars.get("domain") + "/login",
- headers={"Content-Type": "application/json"},
- json={
- "username": "dongfanger",
- "password": "123456",
- }
- )
- assert response.status_code < 400
- self.case_vars.put("token", response.jmespath("token"))
- 必須繼承BaseRequest。
- self.request和requests.request用法完全一致。
- self.case_vars用于在測試用例的測試步驟之間傳遞變量,有get和put兩個操作。
- response.jmespath("token")是封裝了requests.Response后添加的方法,替代jmespath.search("token", response.json())的寫法。
BaseRequest的定義如下:
- import jmespath
- from requests import Response
- from tep.client import request
- class TepResponse(Response):
- def __init__(self, response):
- super().__init__()
- for k, v in response.__dict__.items():
- self.__dict__[k] = v
- def jmespath(self, expression):
- return jmespath.search(expression, self.json())
- class BaseRequest:
- def __init__(self, clazz):
- self.case_vars = clazz.case_vars
- def request(self, method, url, **kwargs):
- response = request(method, url, **kwargs)
- return TepResponse(response)
- TepResponse對requests.Response做了封裝,添加了jmespath方法。
- BaseRequest的__init__在初始化時傳入測試類(test.py里面測試用例的類),提取case_vars對象,這樣接口類繼承以后,就能用self.case_vars來get和put變量了。
- 同時調(diào)用tep.client.request,并把response以封裝后的TepResponse返回。
那么問題來了,如何在接口之間傳遞參數(shù)呢?請看test_login_pay_mvc.py、Login和SearchSku這三段代碼:
- # test_login_pay_mvc.py
- class Test:
- case_vars = TepVars()
- case_vars.vars_ = {
- "domain": "http://127.0.0.1:5000",
- "skuNum": "3"
- }
- def test(self):
- # 登錄
- Login(Test).post()
- # 搜索商品
- SearchSku(Test).get()
- # 添加購物車
- AddCart(Test).post()
- # 下單
- Order(Test).post()
- # 支付
- Pay(Test).post()
- # Login.py
- class Login(BaseRequest):
- def post(self):
- response = self.request(
- "post",
- url=self.case_vars.get("domain") + "/login",
- headers={"Content-Type": "application/json"},
- json={
- "username": "dongfanger",
- "password": "123456",
- }
- )
- assert response.status_code < 400
- self.case_vars.put("token", response.jmespath("token"))
- # SearchSku.py
- class SearchSku(BaseRequest):
- def get(self):
- response = self.request(
- "get",
- url=self.case_vars.get("domain") + "/searchSku",
- headers={"token": self.case_vars.get("token")},
- params={"skuName": "電子書"}
- )
- self.case_vars.put("skuId", response.jmespath("skuId"))
- self.case_vars.put("skuPrice", response.jmespath("price"))
- assert response.status_code < 400
- test_login_pay_mvc.py文件的Test測試用例里面,定義了用例級別的變量:domain。
- Login.py文件通過self.case_vars.get("domain")獲取domain,然后把token通過self.case_vars.put("token", response.jmespath("token"))存入token。
- SearchSku.py文件通過self.case_vars.get("token")獲取token,并用self.case_vars.put()存入skuId和skuPrice,供下一個接口AddCart使用,以此類推。
用例數(shù)據(jù)分離的寫法遵從了MVC架構,并借鑒了HttpRunner和JMeter的部分設計,相比于用例數(shù)據(jù)一體,可能編寫體驗沒有那么方便,但是維護性和可復用性都很高,其中接口類的get和post可以根據(jù)數(shù)據(jù)需要定義多個如get_a和post_b等,以支持多種數(shù)據(jù),略微有一丟丟復雜,適合有代碼經(jīng)驗的同學。
如何才能使用用例數(shù)據(jù)一體、用例數(shù)據(jù)分離、HttpRunner3種開發(fā)模式呢?等tep0.9.4版本發(fā)布后進行升級,用tep startproject demo命令創(chuàng)建項目腳手架,就會包含登錄到下單流程的全部3種開發(fā)模式示例代碼,開箱即用,一鍵運行成功。
從此刻開始,tep實用性已經(jīng)提升了一個檔次,但是我將停下它的腳步,將注意力放到HttpRunner3的源碼學習中,并產(chǎn)出一系列文章,站在巨人的肩膀上,才能看得更遠。為什么HttpRunner如此重要?因為畢竟tep的技術就是從HttpRunner學來的。