環(huán)境準(zhǔn)備
- Python 3.6 + pycharm
- 用到的庫:requests,HTMLTestRunner塘秦,unittest
requests:HTTP for Humans建蹄,官網(wǎng)(http://docs.python-requests.org/en/master/)解釋就是牛掰碌更,就是處理http請求的裕偿,關(guān)于http請求,可以看文章:http://www.reibang.com/p/b8f9f89d8869痛单;HTMLTestRunner運(yùn)行測試集用例并生成html測試報(bào)告击费;unittest Python單元測試框架。
開始搭框架
整個(gè)框架的結(jié)構(gòu)是這樣的桦他,很簡單但是夠清晰:
- api_call:里面的文件是對服務(wù)端提供的接口進(jìn)行封裝蔫巩,至于為什么中間多出兩級distribute>vo2是為了對版本進(jìn)行很好的標(biāo)志和控制,v02代表我們項(xiàng)目現(xiàn)在是2.0.X 的版本快压,下面是獲取用戶token的接口封裝例子:
class GetAccessToken(Http):
def getAdminToken(self, username):
body = {
}
response = self.post("/api/v1/oauth/token", body)
return response
def getUserToken(self, phone):
body = {
}
response = self.post("/api/v1/oauth/token", body)
return response
該類繼承自Http,Http類的封裝見下文
- cof:對公用類的封裝圆仔,host文件主要是配置host和port,http是對http常用方法及返回結(jié)果的處理蔫劣,restful文件是對restful接口返回結(jié)果的處理坪郭。
(1)Http.py下面是對post方法的封裝
class Http():
"""
對http的常用請求方法進(jìn)行封裝
"""
def init(self):
self.host = ""
self.port = ""
# 默認(rèn)的header,內(nèi)容遵循restful接口規(guī)范要求
self.header = {
"Accept": "application/json",
"Content-Type": "application/json"
}
def post(self, url, dictData):
response = requests.post(self.host+url, data=json.dumps(dictData), headers=self.header)
res = dict()
res["request"] = "methon=post" + " " + self.host + ":" + self.port + url + " " + "body = " + json.dumps(dictData)
res["code"] = response.status_code
res["data"] = response.text
return res
init :host和port應(yīng)是從host文件取得脉幢,設(shè)置請求頭
post: 傳的參數(shù)為url和請求體歪沃,請求體的數(shù)字格式為字典,使用requests庫的post方法嫌松,將返回的數(shù)據(jù)作簡單分離處理res["request"]請求的基本信息沪曙, res["code"] = response.status_code請求返回的狀態(tài)嗎,res["data"] = response.text響應(yīng)數(shù)據(jù)萎羔。將這些分離放入一個(gè)字典里并返回字典液走。這一步的處理是為了斷言和出錯(cuò)處理,尤為重要贾陷。
(2)restful.py:restful風(fēng)格接口的返回值處理方法缘眶。對返回的狀態(tài)碼和期望的狀態(tài)碼作比較,如果不一致髓废,給出錯(cuò)誤信息(其實(shí)這部是對撞他碼的斷言)如果一致巷懈,返回正確的數(shù)據(jù),以供數(shù)據(jù)斷言慌洪,在github的代碼中注釋地很詳細(xì)顶燕,文末有地址。
- runner:下面的HTMLTestRunner是Python的一個(gè)測試報(bào)告生成庫蒋譬,格式為html文檔割岛,這里是Python3的版本愉适,runnerTest.py是測試類:
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(rides.rideTest))
fp = open(filename, 'wb')
runner = HTMLTestRunner.HTMLTestRunner(
stream = fp,
title ='順道嘉接口測試報(bào)告',
description = '順道嘉接口測試報(bào)告')
runner.run(suite)#執(zhí)行測試
fp.close()#關(guān)閉文件犯助,否則會無法生成文件
簡單解釋一下:第一步創(chuàng)建測試套,往測試套里添加測試集维咸,運(yùn)行剂买。生成測試文件惠爽,詳細(xì)代碼見github,里邊運(yùn)行完后可以發(fā)送測試郵件給相關(guān)人員瞬哼。
- testcase:測試用例婚肆,直接粘代碼,因?yàn)槲覍懘a真的很愛注釋(菜鳥)
class rideTest(unittest.TestCase):
def setUp(self):
"""
測試類的構(gòu)造方法
該方法會在每個(gè)case運(yùn)行前被調(diào)用一次
"""
# 實(shí)例化接口調(diào)用對象
self.rides = rides.Rides()
self.carpool = carpool.Carpool()
# 隨機(jī)數(shù)對象
self.rnd = CoRandM.CoRand()
self.rf = CoRestful.Restful()
self.nowTime = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
# 記錄創(chuàng)建資源的標(biāo)志
self.flag = 0
response_createOrder = self.rides.postPassengerOrder("88881713123", 4, 16, self.nowTime)
message = "創(chuàng)建訂單失敗"
code = 201
data_dict = self.rf.parse_response(response_createOrder, code, message)
self.ret_id = data_dict['id']
self.set_create_flag()
def set_create_flag(self):
"""
將資源創(chuàng)建標(biāo)志設(shè)置為1坐慰,表示有創(chuàng)建過資源
"""
self.flag = 1
print("創(chuàng)建資源 %s 成功" % str(self.ret_id))
def tearDown(self):
"""
測試類的析構(gòu)方法
該方法會在每個(gè)case運(yùn)行后被調(diào)用一次
"""
# 刪除服務(wù)较性,回收數(shù)據(jù)
if self.flag != 0:
response_del = self.rides.deleteRide(self.ret_id)
message = "刪除訂單失敗"
code = 204
data_dec_del = self.rf.parse_response(response_del, code, message)
self.flag = 0
print("刪除資源 %s 成功" % str(self.ret_id))
def test_fromPendingToRate_ok(self):
driverPhone = "88889876123"
driverID = 185
routeID = 16
#這里是調(diào)用api_call里面的相應(yīng)方法
driverInlineResponse = self.carpool.makeDriverOnline(driverPhone, routeID)
# 給出錯(cuò)誤信息和期望的狀態(tài)碼,若返回的狀態(tài)碼不合預(yù)期结胀,則測試失敗赞咙,返回錯(cuò)誤信息,若合糟港,則返回響應(yīng)數(shù)據(jù)
message = "司機(jī)出車失敗"
code = 201
response_dict = self.rf.parse_response(driverInlineResponse, code, message)
# 返回?cái)?shù)據(jù)完整性斷言
assert_that(response_dict, has_key('id'))
assert_that(response_dict, has_key('route_id'))
assert_that(response_dict, has_key('status'))
assert_that(response_dict, has_key('passengers'))
assert_that(response_dict, has_key('fee'))
assert_that(response_dict, has_key('route_direction'))
# 返回?cái)?shù)據(jù)正確性斷言
assert_that(response_dict['id'], equal_to(self.ret_id))
assert_that(response_dict['route_id'], equal_to(routeID))
assert_that(response_dict['status'], equal_to("accepted"))
def test_fromPendingToRate_ok(self):是一個(gè)測試用例攀操,我這里是一個(gè)訂單從創(chuàng)建到完成的用例,創(chuàng)建放在setup方法里實(shí)現(xiàn)秸抚,因?yàn)樯婕暗綌?shù)據(jù)的銷貨速和,放在測試用例里數(shù)據(jù)里,當(dāng)多個(gè)用例一起執(zhí)行時(shí)剥汤,資源沒法唯一正確標(biāo)識颠放。 def setUp(self):每個(gè)用例執(zhí)行前都會執(zhí)行該方法,可以把一些常量在該方法中初始化吭敢。def tearDown(self):每個(gè)用例執(zhí)行后都會執(zhí)行該方法慈迈,可作數(shù)據(jù)銷毀操作。
后面的ini文件是環(huán)境配置文件省有,可在此文件中選擇運(yùn)行環(huán)境痒留。
github地址https://github.com/sunsy22/apiTest
又到了搬磚的時(shí)間,點(diǎn)點(diǎn)點(diǎn)不易