基於Requests + Pytest + Yaml + Allure 實現Http協議接口自動化


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報告

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM