Python+request+unittest實(shí)現(xiàn)接口測(cè)試框架集成實(shí)例
1、為什么要寫代碼實(shí)現(xiàn)接口自動(dòng)化
大家知道很多接口測(cè)試工具可以實(shí)現(xiàn)對(duì)接口的測(cè)試,如postman、jmeter、fiddler等等,而且使用方便,那么為什么還要寫代碼實(shí)現(xiàn)接口自動(dòng)化呢?工具雖然方便,但也不足之處:
測(cè)試數(shù)據(jù)不可控制
接口測(cè)試本質(zhì)是對(duì)數(shù)據(jù)的測(cè)試,調(diào)用接口,輸入一些數(shù)據(jù),隨后,接口返回一些數(shù)據(jù)。驗(yàn)證接口返回?cái)?shù)據(jù)的正確性。在用工具運(yùn)行測(cè)試用例之前不得不手動(dòng)向數(shù)據(jù)庫(kù)中插入測(cè)試數(shù)據(jù)。這樣我們的接口測(cè)試是不是就沒有那么“自動(dòng)化了”。
無(wú)法測(cè)試加密接口
這是接口測(cè)試工具的一大硬傷,如我們前面開發(fā)的接口用工具測(cè)試完全沒有問(wèn)題,但遇到需要對(duì)接口參 數(shù)進(jìn)行加密/解密的接口,例如 md5、base64、AES 等常見加密方式。本書第十一章會(huì)對(duì)加密接口進(jìn)行介紹。 又或者接口的參數(shù)需要使用時(shí)間戳,也是工具很難模擬的。
擴(kuò)展能力不足
當(dāng)我們?cè)谙硎芄ぞ咚鶐?lái)的便利的同時(shí),往往也會(huì)受制于工具所帶來(lái)的局限。例如,我想將測(cè)試結(jié)果生 成 HMTL 格式測(cè)試報(bào)告,我想將測(cè)試報(bào)告發(fā)送到指定郵箱。我想對(duì)接口測(cè)試做定時(shí)任務(wù)。我想對(duì)接口測(cè)試做持續(xù)集成。這些需求都是工具難以實(shí)現(xiàn)的。
2、接口自動(dòng)化測(cè)試設(shè)計(jì)
接口測(cè)試調(diào)用過(guò)程可以用下圖概括,增加了測(cè)試數(shù)據(jù)庫(kù)
一般的 接口工具 測(cè)試過(guò)程:
1、接口工具調(diào)用被測(cè)系統(tǒng)的接口(傳參 username="zhangsan")。
2、系統(tǒng)接口根據(jù)傳參(username="zhangsan")向 正式數(shù)據(jù)庫(kù) 中查詢數(shù)據(jù)。
3、將查詢結(jié)果組裝成一定格式的數(shù)據(jù),并返回給被調(diào)用者。
4、人工或通過(guò)工具的斷言功能檢查接口測(cè)試的正確性。
接口自動(dòng)化測(cè)試項(xiàng)目,為了使接口測(cè)試對(duì)數(shù)據(jù)變得可控,測(cè)試過(guò)程如下:
1、接口測(cè)試項(xiàng)目先向 測(cè)試數(shù)據(jù)庫(kù) 中插入測(cè)試數(shù)據(jù)(zhangsan 的個(gè)人信息)。
2、調(diào)用被測(cè)系統(tǒng)接口(傳參 username="zhangsan")。
3、系統(tǒng)接口根據(jù)傳參(username="zhangsan")向測(cè)試數(shù)據(jù)庫(kù)中進(jìn)行查詢并得到 zhangsan 個(gè)人信息。
4、將查詢結(jié)果組裝成一定格式的數(shù)據(jù),并返回給被調(diào)用者。
5、通過(guò)單元測(cè)試框架斷言接口返回的數(shù)據(jù)(zhangsan 的個(gè)人信息),并生成測(cè)試報(bào)告。
為了使正式數(shù)據(jù)庫(kù)的數(shù)據(jù)不被污染,建議使用獨(dú)立的 測(cè)試數(shù)據(jù)庫(kù) 。
2、requests庫(kù)
Requests 使用的是 urllib3,因此繼承了它的所有特性。Requests 支持 HTTP 連接保持和連接池 ,支持 使用cookie保持會(huì)話 ,支持 文件上傳 ,支持 自動(dòng)確定響應(yīng)內(nèi)容的編碼。 對(duì)request庫(kù)的更詳細(xì)的介紹可以看我之前接口測(cè)試基礎(chǔ)的文章:
http://www.dbjr.com.cn/article/122571.htm?pc
http://www.dbjr.com.cn/article/108168.htm
3、接口測(cè)試代碼示例
下面以之前用 python+django 開發(fā)的用戶簽到系統(tǒng)為背景,展示接口測(cè)試的代碼。
為什么開發(fā)接口?開發(fā)的接口主要給誰(shuí)來(lái)用?
前端和后端分離是近年來(lái) Web 應(yīng)用開發(fā)的一個(gè)發(fā)展趨勢(shì)。這種模式將帶來(lái)以下優(yōu)勢(shì):
1、后端可以不用必須精通前端技術(shù)(HTML/JavaScript/CSS),只專注于數(shù)據(jù)的處理,對(duì)外提供 API 接口。
2、前端的專業(yè)性越來(lái)越高,通過(guò) API 接口獲取數(shù)據(jù),從而專注于頁(yè)面的設(shè)計(jì)。
3、前后端分離增加接口的應(yīng)用范圍,開發(fā)的接口可以應(yīng)用到 Web 頁(yè)面上,也可以應(yīng)用到移動(dòng) APP 上。
在這種開發(fā)模式下,接口測(cè)試工作就會(huì)變得尤為重要了。
開發(fā)實(shí)現(xiàn)的接口代碼示例:
# 添加發(fā)布會(huì)接口實(shí)現(xiàn) def add_event(request): eid = request.POST.get('eid','') # 發(fā)布會(huì)id name = request.POST.get('name','') # 發(fā)布會(huì)標(biāo)題 limit = request.POST.get('limit','') # 限制人數(shù) status = request.POST.get('status','') # 狀態(tài) address = request.POST.get('address','') # 地址 start_time = request.POST.get('start_time','') # 發(fā)布會(huì)時(shí)間 if eid =='' or name == '' or limit == '' or address == '' or start_time == '': return JsonResponse({'status':10021,'message':'parameter error'}) result = Event.objects.filter(id=eid) if result: return JsonResponse({'status':10022,'message':'event id already exists'}) result = Event.objects.filter(name=name) if result: return JsonResponse({'status':10023,'message':'event name already exists'}) if status == '': status = 1 try: Event.objects.create(id=eid,name=name,limit=limit,address=address,status=int(status),start_time=start_time) except ValidationError: error = 'start_time format error. It must be in YYYY-MM-DD HH:MM:SS format.' return JsonResponse({'status':10024,'message':error}) return JsonResponse({'status':200,'message':'add event success'})
通過(guò)POST請(qǐng)求接收發(fā)布會(huì)參數(shù):發(fā)布會(huì)id、標(biāo)題、人數(shù)、狀態(tài)、地址和時(shí)間等參數(shù)。
首先,判斷eid、name、limit、address、start_time等字段均不能為空,否則JsonResponse()返回相應(yīng)的狀態(tài)碼和提示。JsonResponse()是一個(gè)非常有用的方法,它可以直接將字典轉(zhuǎn)化成Json格式返回到客戶端。
接下來(lái),判斷發(fā)布會(huì)id是否存在,以及發(fā)布會(huì)名稱(name)是否存在;如果存在將返回相應(yīng)的狀態(tài)碼和 提示信息。
再接下來(lái),判斷發(fā)布會(huì)狀態(tài)是否為空,如果為空,將狀態(tài)設(shè)置為1(True)。
最后,將數(shù)據(jù)插入到 Event 表,在插入的過(guò)程中如果日期格式錯(cuò)誤,將拋出 ValidationError 異常,接收 該異常并返回相應(yīng)的狀態(tài)和提示,否則,插入成功,返回狀態(tài)碼200和“add event success”的提示。
# 發(fā)布會(huì)查詢接口實(shí)現(xiàn) def get_event_list(request): eid = request.GET.get("eid", "") # 發(fā)布會(huì)id name = request.GET.get("name", "") # 發(fā)布會(huì)名稱 if eid == '' and name == '': return JsonResponse({'status':10021,'message':'parameter error'}) if eid != '': event = {} try: result = Event.objects.get(id=eid) except ObjectDoesNotExist: return JsonResponse({'status':10022, 'message':'query result is empty'}) else: event['eid'] = result.id event['name'] = result.name event['limit'] = result.limit event['status'] = result.status event['address'] = result.address event['start_time'] = result.start_time return JsonResponse({'status':200, 'message':'success', 'data':event}) if name != '': datas = [] results = Event.objects.filter(name__contains=name) if results: for r in results: event = {} event['eid'] = r.id event['name'] = r.name event['limit'] = r.limit event['status'] = r.status event['address'] = r.address event['start_time'] = r.start_time datas.append(event) return JsonResponse({'status':200, 'message':'success', 'data':datas}) else: return JsonResponse({'status':10022, 'message':'query result is empty'})
通過(guò)GET請(qǐng)求接收發(fā)布會(huì)id和name 參數(shù)。兩個(gè)參數(shù)都是可選的。首先,判斷當(dāng)兩個(gè)參數(shù)同時(shí)為空,接口返回狀態(tài)碼10021,參數(shù)錯(cuò)誤。
如果發(fā)布會(huì)id不為空,優(yōu)先通過(guò)id查詢,因?yàn)閕d的唯一性,所以,查詢結(jié)果只會(huì)有一條,將查詢結(jié)果 以 key:value 對(duì)的方式存放到定義的event字典中,并將數(shù)據(jù)字典作為整個(gè)返回字典中data對(duì)應(yīng)的值返回。
name查詢?yōu)槟:樵?,查詢?shù)據(jù)可能會(huì)有多條,返回的數(shù)據(jù)稍顯復(fù)雜;首先將查詢的每一條數(shù)據(jù)放到一 個(gè)字典event中,再把每一個(gè)字典再放到數(shù)組datas中,最后再將整個(gè)數(shù)組做為返回字典中data對(duì)應(yīng)的值返回。
接口測(cè)試代碼示例
#查詢發(fā)布會(huì)接口測(cè)試代碼 import requests url = "http://127.0.0.1:8000/api/get_event_list/" r = requests.get(url, params={'eid':'1'}) result = r.json() print(result) assert result['status'] == 200 assert result['message'] == "success" assert result['data']['name'] == "xx 產(chǎn)品發(fā)布會(huì)" assert result['data']['address'] == "北京林匹克公園水立方" assert result['data']['start_time'] == "2016-10-15T18:00:00"
因?yàn)椤鞍l(fā)布會(huì)查詢接口”是GET類型,所以,通過(guò)requests庫(kù)的get()方法調(diào)用,第一個(gè)參數(shù)為調(diào)用接口的URL地址,params設(shè)置接口的參數(shù),參數(shù)以字典形式組織。
json()方法可以將接口返回的json格式的數(shù)據(jù)轉(zhuǎn)化為字典。
接下來(lái)就是通過(guò) assert 語(yǔ)句對(duì)接字典中的數(shù)據(jù)進(jìn)行斷言。分別斷言status、message 和data的相關(guān)數(shù)據(jù)等。
使用unittest單元測(cè)試框架開發(fā)接口測(cè)試用例
#發(fā)布會(huì)查詢接口測(cè)試代碼 import unittest import requests class GetEventListTest(unittest.TestCase): def setUp(self): self.base_url = "http://127.0.0.1:8000/api/get_event_list/" def test_get_event_list_eid_null(self): ''' eid 參數(shù)為空 ''' r = requests.get(self.base_url, params={'eid':''}) result = r.json() self.assertEqual(result['status'], 10021) self.assertEqual(result['message'], 'parameter error') def test_get_event_list_eid_error(self): ''' eid=901 查詢結(jié)果為空 ''' r = requests.get(self.base_url, params={'eid':901}) result = r.json() self.assertEqual(result['status'], 10022) self.assertEqual(result['message'], 'query result is empty') def test_get_event_list_eid_success(self): ''' 根據(jù) eid 查詢結(jié)果成功 ''' r = requests.get(self.base_url, params={'eid':1}) result = r.json() self.assertEqual(result['status'], 200) self.assertEqual(result['message'], 'success') self.assertEqual(result['data']['name'],u'mx6發(fā)布會(huì)') self.assertEqual(result['data']['address'],u'北京國(guó)家會(huì)議中心') def test_get_event_list_nam_result_null(self): ''' 關(guān)鍵字‘a(chǎn)bc'查詢 ''' r = requests.get(self.base_url, params={'name':'abc'}) result = r.json() self.assertEqual(result['status'], 10022) self.assertEqual(result['message'], 'query result is empty') def test_get_event_list_name_find(self): ''' 關(guān)鍵字‘發(fā)布會(huì)'模糊查詢 ''' r = requests.get(self.base_url, params={'name':'發(fā)布會(huì)'}) result = r.json() self.assertEqual(result['status'], 200) self.assertEqual(result['message'], 'success') self.assertEqual(result['data'][0]['name'],u'mx6發(fā)布會(huì)') self.assertEqual(result['data'][0]['address'],u'北京國(guó)家會(huì)議中心') 49if __name__ == '__main__': unittest.main()
unittest單元測(cè)試框架可以幫助 組織和運(yùn)行接口測(cè)試用例。
4、接口自動(dòng)化測(cè)試框架實(shí)現(xiàn)
關(guān)于接口自動(dòng)化測(cè)試,unittest 已經(jīng)幫我們做了大部分工作,接下來(lái)只需要 集成數(shù)據(jù)庫(kù)操作 ,以及 HTMLTestRunner測(cè)試報(bào)告生成 擴(kuò)展即可。
框架結(jié)構(gòu)如下圖:
pyrequests 框架:
db_fixture/: 初始化接口測(cè)試數(shù)據(jù)。
interface/: 用于編寫接口自動(dòng)化測(cè)試用例。
report/: 生成接口自動(dòng)化測(cè)試報(bào)告。
db_config.ini : 數(shù)據(jù)庫(kù)配置文件。
HTMLTestRunner.py unittest 單元測(cè)試框架擴(kuò)展,生成 HTML 格式的測(cè)試報(bào)告。
run_tests.py : 執(zhí)行所有接口測(cè)試用例。
4.1、數(shù)據(jù)庫(kù)配置
首先,需要修改被測(cè)系統(tǒng)將數(shù)據(jù)庫(kù)指向測(cè)試數(shù)據(jù)庫(kù)。以 MySQL數(shù)據(jù)庫(kù)為例,針對(duì) django 項(xiàng)目而言,修改.../guest/settings.py 文件。可以在系統(tǒng)測(cè)試環(huán)境單獨(dú)創(chuàng)建一個(gè)測(cè)試庫(kù)。 這樣做的目的是讓接口測(cè)試的數(shù)據(jù)不會(huì)清空或污染到功能測(cè)試庫(kù)的數(shù)據(jù)。 其他框架開發(fā)的項(xiàng)目與django項(xiàng)目類似,這個(gè)工作一般由開發(fā)同學(xué)完成,我們測(cè)試同學(xué)更多關(guān)注的是測(cè)試框架的代碼。
4.2、框架代碼實(shí)現(xiàn)
4.2.1、首先,創(chuàng) 建數(shù)據(jù)庫(kù)配置文件.../db_config.ini
4.2.2、接下來(lái), 簡(jiǎn)單封裝數(shù)據(jù)庫(kù)操作,數(shù)據(jù)庫(kù)表數(shù)據(jù)的插入和清除 ,.../db_fixture/ mysql_db.py
import pymysql.cursors import os import configparser as cparser # ======== Reading db_config.ini setting =========== base_dir = str(os.path.dirname(os.path.dirname(__file__))) base_dir = base_dir.replace('\\', '/') file_path = base_dir + "/db_config.ini" cf = cparser.ConfigParser() cf.read(file_path) host = cf.get("mysqlconf", "host") port = cf.get("mysqlconf", "port") db = cf.get("mysqlconf", "db_name") user = cf.get("mysqlconf", "user") password = cf.get("mysqlconf", "password") # ======== MySql base operating =================== class DB: def __init__(self): try: # Connect to the database self.connection = pymysql.connect(host=host, port=int(port), user=user, password=password, db=db, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) except pymysql.err.OperationalError as e: print("Mysql Error %d: %s" % (e.args[0], e.args[1])) # clear table data def clear(self, table_name): # real_sql = "truncate table " + table_name + ";" real_sql = "delete from " + table_name + ";" with self.connection.cursor() as cursor: cursor.execute("SET FOREIGN_KEY_CHECKS=0;") cursor.execute(real_sql) self.connection.commit() # insert sql statement def insert(self, table_name, table_data): for key in table_data: table_data[key] = "'"+str(table_data[key])+"'" key = ','.join(table_data.keys()) value = ','.join(table_data.values()) real_sql = "INSERT INTO " + table_name + " (" + key + ") VALUES (" + value + ")" #print(real_sql) with self.connection.cursor() as cursor: cursor.execute(real_sql) self.connection.commit() # close database def close(self): self.connection.close() # init data def init_data(self, datas): for table, data in datas.items(): self.clear(table) for d in data: self.insert(table, d) self.close() if __name__ == '__main__': db = DB() table_name = "sign_event" data = {'id':1,'name':'紅米','`limit`':2000,'status':1,'address':'北京會(huì)展中心','start_time':'2016-08-20 00:25:42'} table_name2 = "sign_guest" data2 = {'realname':'alen','phone':12312341234,'email':'alen@mail.com','sign':0,'event_id':1} db.clear(table_name) db.insert(table_name, data) db.close()
首先,讀取 db_config.ini 配置文件。 創(chuàng)建 DB 類,__init__()方法初始化,通過(guò) pymysql.connect()連接數(shù)據(jù)庫(kù)。
因?yàn)檫@里只用到數(shù)據(jù)庫(kù)表的清除和插入,所以只創(chuàng)建 clear()和 insert()兩個(gè)方法。其中,insert()方法對(duì)數(shù) 據(jù)的插入做了簡(jiǎn)單的格式轉(zhuǎn)化,可將字典轉(zhuǎn)化成 SQL 插入語(yǔ)句,這樣格式轉(zhuǎn)化了方便了數(shù)據(jù)庫(kù)表數(shù)據(jù)的創(chuàng)建。
最后,通過(guò) close()方法用于關(guān)閉數(shù)據(jù)庫(kù)連接。
4.2.3、接下來(lái)接下來(lái) 創(chuàng)建測(cè)試數(shù)據(jù) ,.../db_fixture/ test_data.py
import sys sys.path.append('../db_fixture') try: from mysql_db import DB except ImportError: from .mysql_db import DB # create data datas = { 'sign_event':[ {'id':1,'name':'紅米Pro發(fā)布會(huì)','`limit`':2000,'status':1,'address':'北京會(huì)展中心','start_time':'2017-08-20 14:00:00'}, {'id':2,'name':'可參加人數(shù)為0','`limit`':0,'status':1,'address':'北京會(huì)展中心','start_time':'2017-08-20 14:00:00'}, {'id':3,'name':'當(dāng)前狀態(tài)為0關(guān)閉','`limit`':2000,'status':0,'address':'北京會(huì)展中心','start_time':'2017-08-20 14:00:00'}, {'id':4,'name':'發(fā)布會(huì)已結(jié)束','`limit`':2000,'status':1,'address':'北京會(huì)展中心','start_time':'2001-08-20 14:00:00'}, {'id':5,'name':'小米5發(fā)布會(huì)','`limit`':2000,'status':1,'address':'北京國(guó)家會(huì)議中心','start_time':'2017-08-20 14:00:00'}, ], 'sign_guest':[ {'id':1,'realname':'alen','phone':13511001100,'email':'alen@mail.com','sign':0,'event_id':1}, {'id':2,'realname':'has sign','phone':13511001101,'email':'sign@mail.com','sign':1,'event_id':1}, {'id':3,'realname':'tom','phone':13511001102,'email':'tom@mail.com','sign':0,'event_id':5}, ], } # Inster table datas def init_data(): DB().init_data(datas) if __name__ == '__main__': init_data()
init_data()函數(shù)用于讀取 datas 字典中的數(shù)據(jù),調(diào)用 DB 類中的 clear()方法清除數(shù)據(jù)庫(kù),然后,調(diào)用 insert() 方法插入表數(shù)據(jù)。
4.2.4、編寫 接口測(cè)試用例 。創(chuàng)建添加發(fā)布會(huì)接口測(cè)試文件.../interface/ add_event_test.py
import unittest import requests import os, sys parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, parentdir) from db_fixture import test_data class AddEventTest(unittest.TestCase): ''' 添加發(fā)布會(huì) ''' def setUp(self): self.base_url = "http://127.0.0.1:8000/api/add_event/" def tearDown(self): print(self.result) def test_add_event_all_null(self): ''' 所有參數(shù)為空 ''' payload = {'eid':'','':'','limit':'','address':"",'start_time':''} r = requests.post(self.base_url, data=payload) self.result = r.json() self.assertEqual(self.result['status'], 10021) self.assertEqual(self.result['message'], 'parameter error') def test_add_event_eid_exist(self): ''' id已經(jīng)存在 ''' payload = {'eid':1,'name':'一加4發(fā)布會(huì)','limit':2000,'address':"深圳寶體",'start_time':'2017'} r = requests.post(self.base_url, data=payload) self.result = r.json() self.assertEqual(self.result['status'], 10022) self.assertEqual(self.result['message'], 'event id already exists') def test_add_event_name_exist(self): ''' 名稱已經(jīng)存在 ''' payload = {'eid':11,'name':'紅米Pro發(fā)布會(huì)','limit':2000,'address':"深圳寶體",'start_time':'2017'} r = requests.post(self.base_url,data=payload) self.result = r.json() self.assertEqual(self.result['status'], 10023) self.assertEqual(self.result['message'], 'event name already exists') def test_add_event_data_type_error(self): ''' 日期格式錯(cuò)誤 ''' payload = {'eid':11,'name':'一加4手機(jī)發(fā)布會(huì)','limit':2000,'address':"深圳寶體",'start_time':'2017'} r = requests.post(self.base_url,data=payload) self.result = r.json() self.assertEqual(self.result['status'], 10024) self.assertIn('start_time format error.', self.result['message']) def test_add_event_success(self): ''' 添加成功 ''' payload = {'eid':11,'name':'一加4手機(jī)發(fā)布會(huì)','limit':2000,'address':"深圳寶體",'start_time':'2017-05-10 12:00:00'} r = requests.post(self.base_url,data=payload) self.result = r.json() self.assertEqual(self.result['status'], 200) self.assertEqual(self.result['message'], 'add event success') if __name__ == '__main__': test_data.init_data() # 初始化接口測(cè)試數(shù)據(jù) unittest.main()
在測(cè)試接口之前,調(diào)用test_data.py文件中的init_data()方法初始化數(shù)據(jù)庫(kù)中的測(cè)試數(shù)據(jù)。
創(chuàng)建AddEventTest測(cè)試類繼承 unittest.TestCase 類,通過(guò)創(chuàng)建測(cè)試用例,調(diào)用相關(guān)接口,并驗(yàn)證接口返回 的數(shù)據(jù)。
4.2.5、創(chuàng)建 run_tests.py 文件
當(dāng)開發(fā)的接口達(dá)到一定數(shù)量后,就需要考慮 分文件分目錄 的來(lái) 劃分 接口測(cè)試用例,如何批量的執(zhí)行不同文件目錄下的用例呢?unittest單元測(cè)試框架提供的 discover() 方法可以幫助我們做到這一點(diǎn)。并使用 HTMLTestRunner 擴(kuò)展生成 HTML 格式的測(cè)試報(bào)告。
import time, sys sys.path.append('./interface') sys.path.append('./db_fixture') from HTMLTestRunner import HTMLTestRunner import unittest from db_fixture import test_data # 指定測(cè)試用例為當(dāng)前文件夾下的 interface 目錄 test_dir = './interface' discover = unittest.defaultTestLoader.discover(test_dir, pattern='*_test.py') if __name__ == "__main__": test_data.init_data() # 初始化接口測(cè)試數(shù)據(jù) now = time.strftime("%Y-%m-%d %H_%M_%S") filename = './report/' + now + '_result.html' fp = open(filename, 'wb') runner = HTMLTestRunner(stream=fp, title='Guest Manage System Interface Test Report', description='Implementation Example with: ') runner.run(discover) fp.close()
首先,通過(guò)調(diào)用test_data.py文件中的init_data()函數(shù)來(lái)初始化接口測(cè)試數(shù)據(jù)。
使用unittest框架所提供的discover()方法,查找 interface/ 目錄下,所有匹配*_test.py 的測(cè)試文件(*星 號(hào)匹配任意字符)。
HTMLTestRunner 為unittest單元測(cè)試框架的擴(kuò)展,利用它所提供的HTMLTestRunner()類來(lái)替換unittest單元測(cè)試框架的TextTestRunner()類,從而生成HTML格式的測(cè)試報(bào)告。
遺憾的是HTMLTestRunner并不支持Python3.x,大家可以在網(wǎng)上找到適用于Python3.x的HTMLTestRunner.py文件,使用在自己的接口自動(dòng)化工程中。
通過(guò) time 的 strftime()方法獲取當(dāng)前時(shí)間,并且轉(zhuǎn)化成一定的時(shí)間格式。作為測(cè)試報(bào)告的名稱。這樣做目的是是為了避免因?yàn)樯傻膱?bào)告的名稱重名而造成報(bào)告的覆蓋。最終,將測(cè)試報(bào)告存放于report/目錄下面。如下圖,一張完整的接口自動(dòng)化測(cè)試報(bào)告。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python實(shí)現(xiàn)將HTML轉(zhuǎn)為PDF/圖片/XML/XPS格式
網(wǎng)頁(yè)內(nèi)容是信息傳播的主要形式之一,這篇文章主要和大家介紹了如何使用Python實(shí)現(xiàn)將HTML分別轉(zhuǎn)為PDF/圖片/XML/XPS格式等,需要的可以參考下2024-03-03python獲取list下標(biāo)及其值的簡(jiǎn)單方法
下面小編就為大家?guī)?lái)一篇python獲取list下標(biāo)及其值的簡(jiǎn)單方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09Python實(shí)現(xiàn)動(dòng)態(tài)圖解析、合成與倒放
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)動(dòng)態(tài)圖的解析、合成與倒放,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Python數(shù)據(jù)結(jié)構(gòu)之單鏈表詳解
這篇文章主要為大家詳細(xì)介紹了Python數(shù)據(jù)結(jié)構(gòu)之單鏈表的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09解決新django中的path不能使用正則表達(dá)式的問(wèn)題
今天小編就為大家分享一篇解決新django中的path不能使用正則表達(dá)式的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12Python中print函數(shù)語(yǔ)法格式以及各參數(shù)舉例詳解
這篇文章主要給大家介紹了關(guān)于Python中print函數(shù)語(yǔ)法格式以及各參數(shù)舉例詳解的相關(guān)資料,print()函數(shù)用于將指定的字符串或?qū)ο?通常是字符串)輸出到屏幕或文件中,需要的朋友可以參考下2023-10-10用Python編寫個(gè)解釋器實(shí)現(xiàn)方法接受
計(jì)算機(jī)只能理解機(jī)器碼。歸根結(jié)底,編程語(yǔ)言只是一串文字,目的是為了讓人類更容易編寫他們想讓計(jì)算機(jī)做的事情。真正的魔法是由編譯器和解釋器完成,它們彌合了兩者之間的差距。解釋器逐行讀取代碼并將其轉(zhuǎn)換為機(jī)器碼2023-01-01Python 合并多個(gè)TXT文件并統(tǒng)計(jì)詞頻的實(shí)現(xiàn)
這篇文章主要介紹了Python 合并多個(gè)TXT文件并統(tǒng)計(jì)詞頻的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08