Github地址 https://github.com/lixiaofeng1993/pytestProject
靈感來源
GitHub上的 pytestDemo 和 HttpRunner
目錄結構
base ==>> requests請求,返回結果類,測試數據對象化封裝
config ==>> 域名,固定變量,數據庫鏈接
public ==>> 測試數據處理,全局變量替換,log,自定義異常類等公共方法
testcase ==>> 測試用例
data.yml ==>> 測試數據
parametrize_query.csv ==>> 參數化數據
用例設計
1.**局限於pytest參數化形式 `@pytest.mark.parametrize` ,每個測試用例只能對應一個參數化文件**
2.保證測試用例py文件的簡潔,每個用例格式基本固定,代碼量少
3.統一的YAML文件格式
4.參數化引用csv文件
用例基本格式
import pytest from public.send_request import SendRequest # 處理發送請求 from public.log import logger from public.sql_to_data import SqlToData # 處理測試數據 from public.help import get_data_path, os, fun_name, report_setting, report_step_setting, allure data_path = get_data_path(os.path.dirname(__file__)) # 返回當前 py 文件的絕對路徑 test_params = SqlToData().yaml_db_query(data_path) # 返回對象化的測試數據 @allure.severity(allure.severity_level.TRIVIAL) # 測試類等級 @allure.epic(test_params.get("epic")) # allure報告一級目錄 @allure.feature(test_params.get("feature")) # allure報告二級目錄 class TestUsersCase: def setup_class(self): self.extract = {} # 全局變量
# 參數化用例格式 @pytest.mark.parametrize("data", test_params["test_register_user_case"].parametrize) # pytest參數化裝飾器 def test_register_user_case(self, data): logger.info("*************** 開始執行用例 ***************") # 獲取執行用例函數名 name = fun_name() # 報告展示的測試步驟 report_step_setting(test_params[name]) test_params[name].parametrize = data result, self.extract = SendRequest(test_params[name], self.extract).send_request() # 報告上展示的測試標題等 report_setting(test_params[name]) logger.info("*************** 結束執行用例 ***************\n")
# 有依賴的參數化用例格式
@pytest.mark.parametrize("data", test_params["test_one_user_case"].parametrize) def test_one_user_case(self, data): logger.info("*************** 開始執行用例 ***************") # 獲取執行用例函數名 name = fun_name() # 報告展示的測試步驟 report_step_setting(test_params[name].case_step_1) # 登錄接口 result, self.extract = SendRequest(test_params[name].case_step_1, self.extract).send_request() report_step_setting(test_params[name]) test_params[name].parametrize = data result, self.extract = SendRequest(test_params[name], self.extract).send_request() # 報告上展示的測試標題等 report_setting(test_params[name]) logger.info("*************** 結束執行用例 ***************\n")
# 非參數化用例格式 def test_all_user_case(self, test_data): logger.info("*************** 開始執行用例 ***************") # 報告展示的測試步驟 report_step_setting(test_data.case_step_1) # 登錄接口 result, self.extract = SendRequest(test_data.case_step_1, self.extract).send_request() report_step_setting(test_data.case_step_2) result, self.extract = SendRequest(test_data.case_step_2, self.extract).send_request() # 報告上展示的測試標題等 report_setting(test_data.case_step_2) logger.info("*************** 結束執行用例 ***************\n")
單接口 YAML 文件參數化
test_register_user_case: path: /register method: post headers: validate: &validate - [ comparator: equal, check: msg, expect: "恭喜,注冊成功!", jsonpath: "$.msg" ] validate_username: &validate_username - [ comparator: equal, check: msg, expect: "用戶名/密碼/手機號不能為空,請檢查!!!", jsonpath: "$.msg" ] validate_username_exit: &validate_username_exit - [ comparator: contains, check: msg, expect: "用戶名已存在", jsonpath: "$.msg" ] validate_phone: &validate_phone - [ comparator: contains, check: msg, expect: "手機號格式不正確", jsonpath: "$.msg" ] validate_sex: &validate_sex - [ comparator: contains, check: msg, expect: "輸入的性別只能是 0(男) 或 1(女)", jsonpath: "$.msg" ] validate_phone_exit: &validate_phone_exit - [ comparator: contains, check: msg, expect: "手機號已被注冊", jsonpath: "$.msg" ] parametrize: - [ username: __name, password: "123456", sex: "__random_int(0, 1)", telephone: __phone, address: __address, validate: *validate ] - [ username: "", password: "123456", sex: "__random_int(0, 1)", telephone: __phone, address: __address, validate: *validate_username ] - [ username: sql_one_user, password: "123456", sex: "__random_int(0, 1)", telephone: __phone, address: __address, validate: *validate_username_exit ] - [ username: __name, password: "123456", sex: "__random_int(0, 1)", telephone: __random_int, address: __address, validate: *validate_phone ] - [ username: __name, password: "123456", sex: "__random_int(2, 9)", telephone: __phone, address: __address, validate: *validate_sex ] - [ username: __name, password: "123456", sex: "__random_int(0, 1)", telephone: sql_one_phone, address: __address, validate: *validate_phone_exit ] params: upload: extract: story: 用例-注冊接口 title: 注冊接口 step: 注冊接口測試 description: 該用例是針對 注冊接口 的測試 sql: sql_one_user: SELECT u.username from `user` u LIMIT 1 sql_one_phone: SELECT u.telephone from `user` u LIMIT 1 epic: 用戶數據測試 feature: 測試Demo
單接口 CSV 文件參數化
test_register_user_case:
path: /register
method: post
headers:
parametrize: ${parametrize_register.csv}
params:
upload:
extract:
story: 用例-注冊接口
title: 注冊接口
step: 注冊接口測試
description: 該用例是針對 注冊接口 的測試
sql:
sql_one_user: SELECT u.username from `user` u LIMIT 1
sql_one_phone: SELECT u.telephone from `user` u LIMIT 1
epic: 用戶數據測試
feature: 測試Demo
parametrize_register.csv文件數據
case_name,username,password,sex,telephone,address,,msg,code
注冊成功,__name,123456,"__random_int(0, 1)",__phone, __address,,"{""comparator"": ""equal"",""expect"": ""恭喜,注冊成功!"",""jsonpath"":""""}","{""comparator"": ""equal"",""expect"": ""0"",""jsonpath"":""""}"
用戶名/密碼/手機號不能為空,,123456,"__random_int(0, 1)",__phone, __address,,"{""comparator"": ""equal"",""expect"": ""用戶名/密碼/手機號不能為空,請檢查!!!"",""jsonpath"":""""}","{""comparator"": ""equal"",""expect"": ""2001"",""jsonpath"":""""}"
用戶名已存在,sql_one_user,123456,"__random_int(0, 1)",__phone, __address,,"{""comparator"": ""contains"",""expect"": ""用戶名已存在"",""jsonpath"":""""}","{""comparator"": ""equal"",""expect"": ""2002"",""jsonpath"":""""}"
手機號格式不正確,__name,123456,"__random_int(0, 1)",__random_int, __address,,"{""comparator"": ""contains"",""expect"": ""手機號格式不正確"",""jsonpath"":""""}","{""comparator"": ""equal"",""expect"": ""2004"",""jsonpath"":""""}"
輸入的性別格式錯誤,__name,123456,"__random_int(2, 9)",__phone, __address,,"{""comparator"": ""contains"",""expect"": ""輸入的性別只能是 0(男) 或 1(女)"",""jsonpath"":""""}","{""comparator"": ""equal"",""expect"": ""2003"",""jsonpath"":""""}"
手機號已被注冊,__name,123456,"__random_int(0, 1)",sql_one_phone, __address,,"{""comparator"": ""contains"",""expect"": ""手機號已被注冊"",""jsonpath"":""""}","{""comparator"": ""equal"",""expect"": ""2005"",""jsonpath"":""""}"
有依賴的接口參數化
test_one_user_case: case_step_1: path: /login method: post headers: parametrize: params: json: username: sql_one_user password: "123456" upload: extract: token: $.login_info.token username: $.login_info.username validate: - [ comparator: equal, check: msg, expect: "恭喜,登錄成功!", jsonpath: "$.msg" ] - [ comparator: equal, check: code, expect: 0, jsonpath: "$.code" ] story: 用例-登錄接口 title: 登錄接口 step: 登錄接口測試 description: 該用例是針對 登錄接口 的測試 path: /get/user method: get headers: token: $token username: $username validate: &validate - [ comparator: equal, check: msg, expect: "查詢成功", jsonpath: "$.msg" ] - [ comparator: equal, check: code, expect: 0, jsonpath: "$.code" ] validate_username: &validate_username - [ comparator: equal, check: msg, expect: "查不到相關用戶的信息", jsonpath: "$.msg" ] - [ comparator: equal, check: code, expect: 1004, jsonpath: "$.code" ] parametrize: - [ username: sql_one_user, validate: *validate ] - [ username: __name, validate: *validate_username ] params: upload: extract: story: 用例-查詢指定用戶信息接口 title: 查詢指定用戶信息接口 step: 查詢指定用戶信息接口測試 description: 該用例是針對 查詢指定用戶信息接口 的測試 sql: sql_one_user: SELECT u.username from `user` u LIMIT 1 epic: 用戶數據測試 feature: 測試Demo
非參數化接口,存在依賴
test_all_user_case:
case_step_1:
path: /login
method: post
headers:
parametrize:
params:
json:
username: sql_one_user
password: "123456"
upload:
extract:
token: $.login_info.token
username: $.login_info.username
validate:
- [ comparator: equal, check: msg, expect: "恭喜,登錄成功!", jsonpath: "$.msg" ]
- [ comparator: equal, check: code, expect: 0, jsonpath: "$.code" ]
story: 用例-登錄接口
title: 登錄接口
step: 登錄接口測試
description: 該用例是針對 登錄接口 的測試
case_step_2:
path: /users
method: get
headers:
token: $token
username: $username
parametrize:
params:
json:
upload:
validate:
- [ comparator: equal, check: msg, expect: "查詢成功", jsonpath: "$.msg" ]
- [ comparator: equal, check: code, expect: 0, jsonpath: "$.code" ]
story: 用例-查詢所有用戶信息接口
title: 查詢所有用戶信息接口
step: 查詢所有用戶信息接口
description: 該用例是針對 查詢所有用戶信息接口 的測試
sql:
sql_one_user: SELECT u.username from `user` u LIMIT 1
epic: 用戶數據測試
feature: 測試Demo
測試數據對象化封裝
def object_data(test_data: dict, file_path: str, case_step_num=10): """ 封裝測試數據為對象 :param test_data: 測試數據 :param file_path: 測試數據文件路徑 :param case_step_num: 測試用例依賴接口數量 :return: 字典包含的數據對象 """ obj = dict() case_step_list = list() case_step_num = int(case_step_num) if str(case_step_num).isdigit() else 10 case_step_num = 10 if case_step_num < 10 else case_step_num for i in range(1, case_step_num + 1): case_step_list.append(f"case_step_{i}") for keys, values in test_data.items(): obj[keys] = ObjectData() if isinstance(values, dict): for key, value in values.items(): setattr(obj[keys], key, value) setattr(obj[keys], "file_path", file_path) if isinstance(value, dict): step = CaseStep() for k, v in value.items(): setattr(step, k, v) setattr(step, "file_path", file_path) if key in case_step_list: setattr(obj[keys], key, step) obj.update({ "epic": test_data.get("epic"), "feature": test_data.get("feature") }) return obj
allure報告