1. 程式人生 > 實用技巧 >介面實戰專案總結上

介面實戰專案總結上

目錄

1. 前程貸業務分析
1.1 平臺介紹

前程貸是一個網貸資訊服務平臺,p2p模式,主要業務流程:借款人釋出借款專案,經管理員稽核,進入競標狀態後,投資人選擇可投資專案進行投資。使用者可以同時為借款人和投資人。

使用者模組:註冊、登入、充值、提現、更新暱稱、投資、使用者資訊,共7個介面

專案模組:新增專案、稽核專案、分頁獲取專案列表,共3個介面

實現5個介面的自動化測試:註冊、登入、充值、新增專案、投資

1.2 資料庫

資料流記錄,5張表

會員表member,儲存平臺會員資料。使用者名稱、密碼(加密)、手機號、使用者型別、可用餘額、註冊時間

專案表loan,儲存平臺專案資料。借款人id,標題,借款金額,年利率,借款期限、借款期限型別、競標天數、建立時間、競標開始時間、結束時間、專案狀態

投資表invest,儲存平臺投資記錄資料,使用者投資後就會在表.;裡新增一條投資資料。投資人id、標id、投資金額、建立時間、是否有效55

回款計劃表repayment,滿標後,每份投資會生成一條或多條回款計劃記錄。

平臺會員資金流水記錄表,只要會員可用餘額有變動,就會在這個表新增一條記錄。

1.3 介面資訊
  • 介面地址、請求方法、請求頭、請求引數

  • 響應體、介面鑑權等

小結:需求分析中,理解業務邏輯,以及資料流在資料庫中的對映關係,明確每個介面的資訊,包括地址、請求方法、型別、請求引數等

2. 測試用例編寫

excel編寫,一個表單代表一個測試模組,用例內容包括:用例編號、名稱、介面地址、請求方法、請求引數(json格式)、預期結果、實際結果、是否通過

測試用例的設計方法:等價類劃分、邊界值分析、錯誤推測法(全形字串、超長混合字串、數字0、單引號)

總共寫了100多條測試用例。

3. 冒煙測試

使用postman對程式的主要功能進行驗證。

4.分層設計理念+資料驅動思想搭建測試框架
  • 結構清晰:測試用例層(業務層)、配置檔案層、資料層、日誌層、報告層、指令碼層
  • 減少程式碼冗餘:資料驅動思想,測試資料與用例執行邏輯分離,一個測試用例指令碼可以對資料進行批量處理

python + unittest + ddt + requests

import unittest

@ddt.ddt  # 裝飾器,該類範圍內會自動建立多個例項方法
class TestRegister(unittest.TestCase): # 新建一個測試類,並繼承unittest.TestCase父類

    @classmethod
    def setUpClass(cls):  # 初始化所有用例的公共操作,建立請求物件,構造請求引數等
        pass

    @classmethod
    def tearDownClass(cls):  # 用於所有用例的公共資源釋放,例如關閉介面請求會話物件
        pass

    @ddt.data(*testdatas)  # 對序列型別拆包,引數傳遞
    def test_register(self, testcase):  # 測試用例:訪問介面、傳參、獲取響應值、斷言操作
		pass 
   
if __name__ == '__main__':
    unittest.main()  # 依據ACSICC值的順序執行
	# 調整執行順序TestSuit套件,呼叫addTest方法,TextTestRunner執行套件
5. 介面自動化測試框架的技術點

請求處理、excel用例讀取、配置資訊的處理、日誌記錄處理、引數化&正則表示式、資料校驗pymysql、介面依賴處理、unittest單元測試框架、ddt資料驅動、Jenkins單元持續整合等

6.封裝—requests介面請求
import json
import requests

class HttpRequest:

    def __init__(self):
        # 建立會話物件,自動化維護cookie資訊
        self.session = requests.Session()

    # 發起請求
    def send(self, method, url, **kwargs):  # 關鍵字引數包括headers、json、cookies等
        method = method.upper()  # 請求方法大寫
        kwargs["json"] = self.handle_param("json", kwargs)
        kwargs["data"] = self.handle_param("data", kwargs)
        return self.session.request(method, url, **kwargs)
    
    # 請求引數處理
    @staticmethod
    def handle_param(param_name, param_dict):
        # 不管輸入的是json格式的字串,還是字典字串,或者是字典,都能轉為字典輸出
        if param_name in param_dict:
            data = param_dict.get(param_name)
            if isinstance(data, str):
                try:
                    data = json.loads(data)  # 將json字串(python中格式為‘{}’)轉換成字典
                except Exception:
                    data = eval(data)  # 直接將字串最外層的引號拿掉,字典形式
            return data

    # 新增請求頭,公共請求頭更新
    def add_headers(self, one_dict):  # 請求頭引數,字典型別
        self.session.headers.update(one_dict)

    # 關閉會話,釋放資源
    def close(self):
        self.session.close()
7. 封裝—excel資料讀寫
import os
from openpyxl import load_workbook

class Testcase:  # 通過建立不同的物件儲存每一條測試用例,用例資料通過建立例項屬性來儲存,具有全域性通用的作用
    pass

class HandleExcel:
    def __init__(self, filename, sheetname=None):
        self.filename = os.path.join(DATA_PATH, filename)
        self.sheetname = sheetname
	
    def read_data(self):
        wb = load_workbook(self.filename)  # 載入excel檔案
        if self.sheetname == None:
            ws = wb.active  # 預設讀取第一個表單
        else:
            ws = wb[self.sheetname]  # 獲取指定表單物件
        testcases_list = [] # 存放資料
        headers_list = []  # 存放表頭資訊
        for row in range(1, ws.max_row + 1):
            one_testcase = Testcase()  # 建立物件,通過動態建立例項屬性的方法存放每一行用例
            for column in range(1, ws.max_column + 1):
                one_cell = ws.cell(row, column) # 建立單元格物件
                one_cell_value = one_cell.value
                if row == 1:
                    headers_list.append(one_cell_value)
                else:
                    key = headers_list[column - 1]  
                    setattr(one_testcase, str(key), one_cell_value)  # 設定當前用例所對應的表頭屬性
                    if key == "actual":
                        setattr(one_testcase, "actual_column", column)  # 設定存放實際響應報文所在列的列號屬性
                    elif key == "result":
                        setattr(one_testcase, "result_column", column)  # 設定存放用例執行結果所在列的列號屬性
            if row != 1: 
                setattr(one_testcase, "row", row)  # 設定當前用例所在的行號屬性
                testcases_list.append(one_testcase)
        return testcases_list # 列表的元素是物件

    def write_data(self, one_testcase, actual_value, result_value):
        wb = load_workbook(self.filename)  # 載入指定excel檔案
        if self.sheetname == None:
            ws = wb.active
        else:
            ws = wb[self.sheetname]  # 訪問表單
        ws.cell(one_testcase.row, one_testcase.actual_column, value=actual_value) # 訪問指定單元格並寫入資料
        ws.cell(one_testcase.row, one_testcase.result_column, value=result_value)  # 寫入狀態時,一定要將excel檔案關閉
        wb.save(self.filename) # 對excel檔案修改後,一定要儲存
8. 封裝—資料庫處理
import random
import pymysql
from scripts.handle_yaml import do_yaml

class HandleMysql:
    def __init__(self):
        # 1.建立連線物件
        self.conn = pymysql.connect(host=do_yaml.get_data('mysql', 'host'),
                                    user=do_yaml.get_data('mysql', 'user'),
                                    password=do_yaml.get_data('mysql', 'password'),
                                    port=do_yaml.get_data('mysql', 'port'),
                                    database=do_yaml.get_data('mysql', 'database'),
                                    charset="utf8",  # 注意這裡不能寫成utf-8
                                    cursorclass=pymysql.cursors.DictCursor)
        self.cursor = self.conn.cursor()  # 2.建立遊標物件

    # 3.獲取一條資料,字典型別
    def get_one_value(self, sql, args=None):
        self.cursor.execute(sql, args=args)
        self.conn.commit()
        return self.cursor.fetchone()
	# 4.獲取多條資料,巢狀字典的列表型別
    def get_values(self, sql, args=None):
        self.cursor.execute(sql, args=args)
        self.conn.commit()
        return self.cursor.fetchall()
	# 5.關閉遊標,再關閉連線
    def close(self):
        self.cursor.close()
        self.conn.close()

    @staticmethod
    def generate_telephone():
        """
        隨機生成手機號
        手機號規則:前3位—網路識別號;第4-7位—地區編碼;第8-11位—使用者號碼
        第1位:1;
        第2位:3,4,5,7,8
        第3位:3:【0,9】, 4:【5,7】, 5:【0,9】, 7:【6,7,8】, 8:【0-9】
        :return:返回一個手機號碼
        """
        # 前三位
        second = random.choice([3, 4, 5, 7, 8])
        third = str({
                        3: random.randint(0, 9),
                        4: random.choice([5, 7]),
                        5: random.randint(0, 9),
                        7: random.choice([6, 7, 8]),
                        8: random.randint(0, 9)
                    }[second])
        # 後八位
        eight = ''.join(random.sample('0123456789', 8))
        return '1' + str(second) + third + eight

    # 在資料庫中查詢隨機生成的手機號是否存在
    def check_telephone(self, telephone):
        sql = do_yaml.get_data('mysql', 'select_user_sql')
        if self.get_one_value(sql, args=[telephone]):
            return True
        else:
            return False

    # 得到一個在資料庫中不存在的手機號
    def get_new_telephone(self):
        while True:
            one_mobile = self.generate_telephone()
            if not self.check_telephone(one_mobile):
                break
        return one_mobile

    def get_not_existed_user_id(self):
        # 從yaml配置檔案中獲取查詢最大使用者id的sql語句
        sql = do_yaml.get_data('mysql', 'select_max_user_id_sql')
        # # 獲取最大的使用者id + 1
        not_existed_id = self.get_one_value(sql).get('max(id)') + 1
        return not_existed_id

    def get_not_existed_loan_id(self):
        sql = do_yaml.get_data('mysql', 'select_max_loan_id_sql')
        # # 獲取最大的使用者id + 1
        not_existed_id = self.get_one_value(sql).get('max(id)') + 1
        return not_existed_id