最近接到一個接口自動化測試的case,並展開了一些調研工作,最后發現,使用pytest測試框架並以數據驅動的方式執行測試用例,可以很好的實現自動化測試。這種方式最大的優點在於后續進行用例維護的時候對已有的測試腳本影響很小。當然,pytest還有以下其他優點:
- 可以讓用戶寫出更為緊湊的測試套件;
- 涉及到的樣板代碼並不多,因此用戶能夠容易地編寫和理解各種測試;
- 測試夾具(fixture)函數常被用來向測試函數添加某個參數,並返回不同的值。在pytest中,可以通過使用一個fixture來模塊化另外一個。同時也可以使用多個fixture,在無需重寫測試用例的情況下,將測試覆蓋到所有參數的組合;
- 可擴展性強,pytest有許多實用的插件。例如:pytest-xdist可以在不使用其他測試器的情況下,被用於執行並行測試;pytest-rerunfailures可以在測試失敗后重新運行,而且運行次數和運行之間的延遲時間都是可以設置的;allure/pytest-html生成測試報告;
相比較其它的測試框架,比如Robot Framework(在創建自定義的HTML報告方面比較繁瑣,頂多能用來生成xUnit格式的簡短報告)、UniteTest/PyUnit(需要大量的樣板代碼),pytest更適合作為本次自動化測試的框架。
下面為大家詳細的介紹這種自動化測試的實現過程。
1 前期的准備工作
1.1 接口路徑表
根據接口文檔,將接口的地址和路徑以及請求方式記錄在excel表中,key:接口名稱,type:請求方式,value:接口路徑,第一行baseurl為基本路徑,type不填。接口名稱建議與接口文檔中的接口名稱一致,這樣方便檢查。如果同一個接口有多種請求方式,需要重新填寫一行,type為相對應的請求方式。這樣記錄接口路徑和請求方式是為了方便后面的數據提取和處理。
1.2 測試用例表
測試用例表中主要記錄9列類型的數據,測試模塊:將接口按照模塊進行划分有利於問題的定位和數據的分類;
- 測試模塊:將被測接口按照功能進行模塊划分;
- 用例編號:主要用於記錄用例的條數,建議按照模塊名稱進行命名,如:登錄模塊,用例編號為login_001,login_002;
- 用例標題:記錄測試的內容;
- 前置條件:當被測接口需要其他接口的數據支撐時,在前置條件欄中填入需要的接口數據:如:login_001:token(login_001指用例編號,token指該用例執行后返回的響應參數中token字段的值),前提條件是該接口用例在本條用例之前;
- 測試步驟:方便於模塊用例的執行;
- 請求接口:按照接口路徑表中key的命名填寫,需要請求登錄接口時,就填寫上圖中表中key命名的登錄接口。請求頭部:當請求頭部中有特殊的參數時,比如該接口需要身份驗證authorization字段,而該字段的數據來源於登錄接口返回的token,這種用例的請求頭部應該這樣填寫: Content-Type=application/json,Authorization=<token>;
- 請求數據:填寫測試用例的請求數據,按照key=value的格式記錄,如果需要其他接口的返回數據,在前置條件中加入之后,再填寫請求數據中需要的返回數據即可,如:username =admin,password=zxcvbnm,token=<token>;
- 斷言:根據接口返回的數據進行斷言,主要是驗證返回數據中的某個字段是否正確,也是按照key=value的格式進行填寫;
2 目錄結構及運行流程
2.1 文件目錄結構
- testcase文件夾:存放測試用例表;
- api文件夾:存放接口路徑表;
- common文件夾:common文件中存放通用的數據處理的腳本,如data.py和utlis.py(主要作用是將表中的數據進行處理,后面會進行詳細的說明)、config.py(測試套件的基本配置);
- report文件夾:用於存放測試完成后生成的測試報告;
- conftest.py:屬於pytest的一種全局公用的文件,一些通用的方法可以放在conftest.py中;
- pytest.ini:pytest的配置文件;
2.2 測試的運行流程
觸發自動化測試之后,測試數據的提取與處理並不會使用到pytest框架,當把數據處理為測試套件后,按模塊分配給pytest進行執行,包括測試模塊、http請求、斷言。所有模塊執行完之后將測試結果體現在生成的測試報告report.html中。測試結束之后可以通過郵件或者釘釘機器人的方式通知測試或開發本次自動化測試的測試結果。
3 測試用例的實現過程
下面簡單介紹一下測試用例實現過程中部分腳本的作用。
3.1 讀取excel表
使用xlrd庫讀取excel表中的內容,python中還有很多可以對excel的數據進行操作的庫,如:openpyxl、xlsxwriter;循環遍歷每一行的數據,保存為列表並賦值給self.list_data。
# -*- coding: utf-8 -*-
import xlrd
class Excel(object):
def __init__(self, file_name):
# 讀取excel
self.wb = xlrd.open_workbook(file_name)
self.sh = self.wb.sheet_names()
self.list_data = []
def read(self):
for sheet_name in self.sh:
sheet = self.wb.sheet_by_name(sheet_name)
rows = sheet.nrows
for i in range(0, rows):
rowvalues = sheet.row_values(i)
self.list_data.append(rowvalues)
3.2 將數據格式化成測試套件
第一步將表格數據保存為列表后,還不是我們需要的數據格式,這樣的數據列表不能直接使用,這里進行了一次數據的格式化。這里需要用到config.py中的case_header的配置,將中文的標題換成英文,並作為字典的key值。之后從第1個元素開始循環遍歷data,將data中每個元素都以 [{'key1': 'value1', 'key2': 'value2'}, {}, {}, ...]的形式保存為list_dict_data並返回。
def data_to_dict(data):
"""
:param data: data_list
:return:
"""
head = []
list_dict_data = []
for d in data[0]:
d = case_header.get(d, d)
head.append(d)
for b in data[1:]:
dict_data = {}
for i in range(len(head)):
if isinstance(b[i], str):
dict_data[head[i]] = b[i].strip()
else:
dict_data[head[i]] = b[i]
list_dict_data.append(dict_data)
return list_dict_data
case_header = {
'測試模塊': 'module',
'用例編號': 'id',
'用例標題': 'title',
'前置條件': 'condition',
'測試步驟': 'step',
'請求接口': 'api',
'請求方式': 'method',
'請求頭部': 'headers',
'請求數據': 'data',
'斷言': 'assert',
'步驟結果': 'score' }
3.3 生成可執行的測試套件
上一步已經將數據處理成了[{'key1': 'value1', 'key2': 'value2'}, {}, {}, ...]這樣的格式,但是發現這樣的格式沒有將模塊中的用例整合到一起,列表中每一個元素都是單獨的一條用例,這樣的話不利於用例的執行,所以,對上一步返回的數據再進行一次處理。因為測試用例和接口路徑是保存在兩個excel表中的,所以需要將兩個表的數據進行合並。首先將接口路徑表中的數據讀取出來並處理成需要的格式 {'key': {'type': 'value', 'url': 'value'}},之后按照測試步驟中的順序把測試用例保存在steps字典中。由於代碼過長,下面只展示核心部分。
for d in data:
# 將請求數據和斷言數據格式化
for key in ('data', 'assert', 'headers'):
if d[key].strip():
test_data = dict()
for i in d[key].split(','):
i = i.split('=')
test_data[i[0]] = i[1]
d[key] = test_data
if d['module'].strip():
if testcase:
testsuite.append(testcase)
testcase = {}
testcase['module'] = d['module']
testcase['steps'] = []
no = str(d['step']).strip()
if no:
step = {'no': str(int(d['step']))}
for key in ('id', 'title', 'condition', 'api', 'headers', 'data', 'assert'):
if key == 'api':
step[key] = {'type': apis[d.get(key, '')]['type'],
'url': apis['baseurl']['url'] + apis[d.get(key, '')]['url']}
else:
step[key] = d.get(key, '')
testcase['steps'].append(step)
if testcase:
testsuite.append(testcase)
3.4 pytest執行測試套件
將http請求封裝在了conftest.py中,使用pytest數據驅動的特點,在執行測試文件test_login。py中不需要import就可以直接調用。這里只展示了發起post請求的代碼,其它類型的請求類似,pytest.fixture通過固定參數request傳遞數據。然后使用'標記'中的pytest.mark.parametrize進行參數化和數據驅動更靈活。在fixture中的方法里面准備測試數據和前置依賴方法,在測試方法中參數化,測試方法調用准備數據和前置方法。pytest.mark.parametrize('post_request', data, indirect=True),indirect=True是把post_request當作函數去執行,data則是前面生成的模塊的測試用例,其中包括了發起\http請求所需要的所有參數。
@pytest.fixture()
def post_request(request):
data = request.param['data']
header = request.param['headers']
url = request.param['api']['url']
no = request.param['no']
logger.info(f'request: {data}')
response = requests.request('POST', url=url, headers=header, data=json.dumps(data))
logger.info(f'response: {response.json()}')
return response, no
# -*- coding: UTF-8 -*-
import pytest
import allure
from common.data import module_data
class TestCase(object):
@allure.feature('登錄')
@pytest.mark.parametrize('post_request', module_data(module='登錄'), indirect=True)
def test_login(self, post_request):
response = post_request[0].json()
no = int(post_request[1])
assert response['msg'] == module_data(module='登錄')[no - 1]['assert']['msg']
def module_data(module):
excel = Excel(file_path.parent / 'testcase/testcase.xlsx')
excel.read()
cases = excel.list_data
test_suit = suite_cases(data_to_dict(cases))
for _ in test_suit:
if _['module'] == module:
data = _['steps']
return data
3.5 運行測試用例
可以在pytest.ini中配置執行測試時的一些文件、類、方法的匹配規則和常用的命令參數,執行時只需要命令行輸入D:\py_test>pytest就可以開始執行自動化測試。也可以不用pytest.ini配置,命令行執行D:\py_test>pytest -s test_login.py --html=report/report.html,-s參數:輸出所有測試用例的print信息,安裝了pytest-html插件后,在執行命令中加入--html=測試報告保存路徑。
pytest.ini文件配置如下:
[pytest]
# 打印print,生成保存報告
addopts = -s --html=report/report.html
# 匹配執行文件
python_files = test_*.py
# 匹配執行類
python_classes = Test*
# 匹配執行方法
python_functions = test_*
3.6 結果展示
可以在ide中執行測試用例,也可以使用命令行執行,執行完測試用例后,會生成一個html格式的測試報告,在瀏覽器中打開就可以查看本次自動化測試的測試結果。pytest不僅支持pytest-html插件,還可以使用allure生成更加美觀的測試報告。下面分別展示使用pytest-html和allure生成的html測試報告,pytest-html報告中記錄的內容比較詳盡,包括了用例運行日志、通過\失敗\跳過用例條數、用例運行時間等等。allure生成的報告可讀性比較強,可以很直觀的看到測試結果。
pytest-html生成的測試報告:
allure生成的測試報告:
4 總結
整個項目完成之后,對pytest測試框架有了更深的理解。同時,pytest也可以使用Jenkins將自動化測試加入到持續集成中,設置定時任務構建或者條件觸發構建等,這樣可以有效的提高測試效率,也節省了人力成本。當然,不僅僅只有這一種實現方式,目前的實現方式還是有很多不足的地方,后面會繼續進行完善和改進。如果你有什么好的建議和方法,歡迎一起進行溝通和交流。
PS:
我們是行者AI,我們在“AI+游戲”中不斷前行。
如果你也對游戲感興趣,對AI充滿好奇,那就快來加入我們(hr@xingzhe.ai)。