Python 接口測試框架

這有多是一個系列文章,也有可能在這裏終止!html

目錄結構定義

首先來看一下項目總體的結構json

框架結構api

代碼結構bash

Excel 文件結構數據結構

代碼詳細解析

1.工具包 tools

封裝操做 excel 方法 excel_operation.pyapp

import xlrd
from config.config import PROJECT_PATH


class OperationExcel:
    def __init__(self, file_name=None, sheet_id=0):
        if file_name:
            self.file_name = PROJECT_PATH + '/data/' + file_name
            self.sheet_id = sheet_id
        else:
            try:
                self.file_name = PROJECT_PATH + '/data/case.xlsx'
                self.sheet_id = 0
            except FileExistsError:
                raise FileExistsError("the default testcase file not found")
        self.book = self.get_book()
        self.data = self.get_data()

    # 獲取工做簿
    def get_book(self):
        book = xlrd.open_workbook(self.file_name)
        return book

    # 獲取 sheets 的內容
    def get_data(self):
        book = self.book
        tables = book.sheets()[self.sheet_id]
        return tables

    # 獲取全部 sheet 的名字
    def get_sheet_name(self):
        book = self.book
        return book.sheet_names()

    # 獲取全部 sheets
    def get_sheets(self):
        book = self.book
        sheets = book.sheets()
        return sheets

    # 獲取某個單元格的內容
    def get_cell_value(self, row, col):
        return self.data.cell_value(row, col)

    # 獲取單元格的行數
    def get_lines(self):
        tables = self.data
        case_rows = tables.nrows - 1
        return case_rows

    # 獲取某一列的內容
    def get_cols_data(self, col_id=None):
        if col_id is not None:
            cols = self.data.col_values(col_id)
        else:
            cols = self.data.col_values(0)
        return cols

    # 獲取某一行的內容
    def get_rows_data(self, row_id=None):
        if row_id is not None:
            rows = self.data.row_values(row_id)
        else:
            rows = self.data.row_values(0)
        return rows

    # 獲取某個 caseid 對應的行號
    def get_row_num(self, case_id):
        num = 0
        cols_data = self.get_cols_data()
        for col_data in cols_data:
            if case_id in col_data:
                return num
            num += 1
複製代碼

使用 xlrd 庫來操做 excel,同時,該類只作最底層的 excel 數據提取,不作任何業務相關的判斷。後面會陸續增長 json,yaml 等數據結構的操做工具。框架

中間數據操做層 operate_data.py函數

from config.config import ExcelConfData


class OperateExcelData(object):
    def get_caseid(self):
        return ExcelConfData.caseid

    def get_url(self):
        return ExcelConfData.url

    def get_method(self):
        return ExcelConfData.method

    def get_is_auto_run(self):
        return ExcelConfData.automated

    def get_header(self):
        return ExcelConfData.header

    def get_data(self):
        return ExcelConfData.data

    def get_casename(self):
        return ExcelConfData.casename

    def get_statuscode(self):
        return ExcelConfData.statuscode

    def get_checkpoints(self):
        return ExcelConfData.checkpoints

    def get_validate(self):
        return ExcelConfData.validate

    def get_caseuniqueid(self):
        return ExcelConfData.caseuniqueid

    def get_authtype(self):
        return ExcelConfData.authtype
複製代碼

屬於操做數據的中間層,從配置文件中拿到咱們定義好的 excel 結構,這樣,若是咱們的 excel 結構有變化,只須要修改配置文件便可工具

配置文件中的 excel 結構以下post

class ExcelConfData:
    caseid = '0'
    casename = '1'
    caselevel = '2'
    preconditions = '3'
    testcontent = '4'
    expect = '5'
    casecategory = '6'
    automated = '7'  # 1 是自動運行, 2 是非自動運行
    caseuniqueid = '1'  # 8
    method = '9'
    url = '10'
    data = '11'
    header = '12'
    statuscode = '13'
    checkpoints = '14'
    validate = '15'
    parameterize = '16'
    result = '17'
    authtype = '18'  # 0:admin, 1:common user, 2:not login
複製代碼

獲取測試文件中數據工具 get_data.py

from tools.excel_operation import OperationExcel
from tools.operate_data import OperateExcelData


class GetExcelData(object):
    def __init__(self, filename=None, sheet_id=0):
        self.operate_excel = OperationExcel(filename, sheet_id)
        self.operate_data = OperateExcelData()

    # 獲取 sheet 個數
    def get_sheets(self):
        sheet_num = self.operate_excel.get_sheets()
        return len(sheet_num)

    # 獲取 excel 行數,即用例個數
    def get_case_lines(self):
        return self.operate_excel.get_lines()

    # 獲取是否執行
    def get_is_auto_run(self, row):
        auto_flag = False
        col = int(self.operate_data.get_is_auto_run())
        run_model = self.operate_excel.get_cell_value(row, col)
        if run_model == 1:
            auto_flag = True
        else:
            auto_flag = False
        return auto_flag

    # 獲取請求方式
    def get_request_method(self, row):
        col = int(self.operate_data.get_method())
        request_method = self.operate_excel.get_cell_value(row, col)
        return request_method

    # 獲取 url
    def get_request_url(self, row):
        col = int(self.operate_data.get_url())
        url = self.operate_excel.get_cell_value(row, col)
        return url

    # 獲取請求數據
    def get_request_data(self, row):
        col = int(self.operate_data.get_data())
        data = self.operate_excel.get_cell_value(row, col)
        return data

    # 獲取 status code
    def get_response_statuscode(self, row):
        col = int(self.operate_data.get_statuscode())
        statuscode = self.operate_excel.get_cell_value(row, col)
        return statuscode

    # 獲取 checkpoints
    def get_checkpoints(self, row):
        col = int(self.operate_data.get_checkpoints())
        checkpoints = self.operate_excel.get_cell_value(row, col)
        return checkpoints

    # 獲取 validate
    def get_validate(self, row):
        col = int(self.operate_data.get_validate())
        validate = self.operate_excel.get_cell_value(row, col)
        return validate

    # 獲取測試用例惟一 ID
    def get_caseuniqueid(self, row):
        col = int(self.operate_data.get_caseuniqueid())
        caseuniqueid = self.operate_excel.get_cell_value(row, col)
        if isinstance(caseuniqueid, float):
            caseuniqueid = int(caseuniqueid)
        return str(caseuniqueid)

    # 獲取 header 信息
    def get_header(self, row):
        col = int(self.operate_data.get_header())
        header = self.operate_excel.get_cell_value(row, col)
        return header

    # 獲取是否須要鑑權信息
    def get_authtype(self, row):
        col = int(self.operate_data.get_authtype())
        authtype = self.operate_excel.get_cell_value(row, col)
        return authtype  
複製代碼

獲取到測試數據中業務相關的數據,例如是否自動化執行,是否使用 header,是否須要鑑權信息等。

通用工具文件 common_util.py

import json
import operator
from config.config import UserInfo, EnvConf
import requests


class CommonUtil(object):
    def is_contain(self, str1, str2):
        """ :param str1: 原始字符串 :param str2: 被查找的字符串 :return: True or False """
        flag = None
        if str1 in str2:
            flag = True
        else:
            flag = False
        return flag

    def is_equal_dict(self, d1, d2):
        if isinstance(d1, str):
            d1 = json.loads(d1)
        if isinstance(d2, str):
            d2 = json.loads(d2)
        return operator.eq(d1, d2)


def adminlogin():
    url = f"http://{EnvConf.host}:{EnvConf.port}/api/user-management/tokens"
    data = UserInfo.admininfo
    resp = requests.post(url=url, json=data)

    try:
        token = f"Bearer {resp.json()['data']['access_token']}"
    except:
        raise

    return token


def commonlogin():
    url = f"http://{EnvConf.host}:{EnvConf.port}/api/user-management/tokens"
    data = UserInfo.commoninfo
    resp = requests.post(url=url, json=data)

    try:
        token = f"Bearer {resp.json()['data']['access_token']}"
    except:
        raise

    return token
複製代碼

主要編寫一些驗證器,或者通用的獲取登錄 token 信息等函數。這裏的驗證器還很簡單,後面再慢慢添加,好比正則校驗,解析 json 校驗等。

2. 基礎包 base

封裝 http 請求 runmethod.py

import requests
import json


class RunMethod(object):
    def __init__(self):
        self.verify = False
        self.headers = None

    def post_main(self, url, data=None, header=None):
        res = None
        if header is not None:
            res = requests.post(url=url, data=data, headers=header)
        else:
            res = requests.post(url=url, data=data)
        return res.json()

    def get_main(self, url, data=None, header=None, param=None):
        res = None
        if header is not None:
            res = requests.get(url=url, data=data, headers=header, verify=self.verify, params=param)
        else:
            res = requests.get(url=url, data=data, verify=self.verify, params=param)
        return res.json()

    def del_main(self, url, data=None, header=None):
        res = None
        if header is not None:
            res = requests.delete(url=url, data=data, headers=header)
        else:
            res = requests.delete(url=url, data=data)
        return res.json()

    def run_main(self, method, url, data=None, header=None):
        res = None
        if method == 'POST':
            res = self.post_main(url, data, header)
        elif method == 'GET':
            res = self.get_main(url, data, header)
        else:
            res = self.del_main(url, data, header)
        return json.dumps(res, ensure_ascii=False, sort_keys=True, indent=2)
複製代碼

當前的封裝仍是很簡陋的,並無過多的異常處理,參數校驗等,後面會對這方面作一下加強。

runmock.py 是用來作 mock 數據的,之後再用。

提取 excel 數據文件 basetest.py

from tools.get_data import GetExcelData
from base.runmethod import RunMethod
from tools.common_util import CommonUtil
from config.config import EnvConf, Header
import json
from tools.excel_operation import OperationExcel
from tools.common_util import adminlogin, commonlogin


class CaseDataAllSheets:
    def __init__(self, filename=None):
        self.filename = filename
        self.opera_excel = OperationExcel(filename)
        self.sheet_nums = self.opera_excel.get_sheets()

    def get_all_sheets_data(self):
        total_data = {
            "sheet-data": [],
            'case_data_ids': []
        }
        for i in range(len(self.sheet_nums)):
            data = {}
            sheet_name = self.opera_excel.get_sheet_name()[i]
            casedata = CaseData(filename=self.filename, sheet_id=i)
            test_data, case_data_ids = casedata.get_testcase_data()
            data[sheet_name] = test_data
            total_data['sheet-data'].append(data)
            total_data['case_data_ids'].append(case_data_ids)
        return total_data


class CaseData:
    def __init__(self, filename=None, sheet_id=0):
        self.exceldata = GetExcelData(filename, sheet_id)
        self.casenums = self.exceldata.get_case_lines()

    def get_testcase_data(self):
        test_data = {
            'parameterize': []
        }
        case_data_ids = []
        for case in range(1, self.casenums + 1):
            if self.exceldata.get_is_auto_run(case):
                case_data_json = {
                    'request-data': {},
                    'response-data': {}
                }
                case_method = self.exceldata.get_request_method(case)
                data_url = self.exceldata.get_request_url(case)
                case_url = f"http://{EnvConf.host}:{EnvConf.port}" + data_url
                case_data = self.exceldata.get_request_data(case)
                if case_data != '':
                    try:
                        case_data = json.loads(case_data)
                    except:
                        raise
                case_header = self.exceldata.get_header(case)
                if case_header == '':
                    case_header = Header.headers
                else:
                    try:
                        case_header = json.loads(case_header)
                    except:
                        raise
                case_statuscode = self.exceldata.get_response_statuscode(case)
                case_checkpoint = self.exceldata.get_checkpoints(case)
                case_validate = self.exceldata.get_validate(case)
                case_uniqueid = self.exceldata.get_caseuniqueid(case)
                print(case_uniqueid)
                print(type(case_uniqueid))
                case_authtype = self.exceldata.get_authtype(case)
                if case_authtype == 0:
                    token = adminlogin()
                    case_header['authorization'] = token
                elif case_authtype == 1:
                    token = commonlogin()
                    case_header['authorization'] = token
                else:
                    pass
                case_data_json['request-data']['url'] = case_url
                case_data_json['request-data']['data'] = case_data
                case_data_json['request-data']['header'] = case_header
                case_data_json['request-data']['method'] = case_method
                case_data_json['response-data']['statuscode'] = case_statuscode
                case_data_json['response-data']['checkpoint'] = case_checkpoint
                case_data_json['response-data']['validate'] = case_validate
                case_data_ids.append(case_uniqueid)
                test_data['parameterize'].append(case_data_json)
        return test_data, case_data_ids
複製代碼

我把真正的處理 excel 測試用例數據的功能放在了這裏,將咱們須要的數據,如:url,請求體 data,請求方法 method 等信息組裝好,放到內存中,供 pytest 參數化時使用。

3. pytest 測試用例代碼

在 case 文件夾中,用來存放真正的 pytest 測試代碼,咱們寫一個簡單的測試代碼 demo

from base.basetest import BaseTest, CaseData
import pytest


class Test_example(BaseTest):
    testcase = CaseData('test.xlsx', 1)
    testdata, ids = testcase.get_testcase_data()

 @pytest.mark.parametrize('autotest', testdata['parameterize'], ids=ids)
    def test_case(self, autotest, casefile):
        res_json = self.runmethod.run_main(autotest['request-data']['method'], autotest['request-data']['url'],
                                           data=autotest['request-data']['data'],
                                           header=autotest['request-data']['header'])
        print(res_json)
        print("casefile", casefile)
        assert self.validate.is_equal_dict(res_json, autotest['response-data']['checkpoint']) is True
複製代碼

這裏時獲取 excel 中的 sheet 序號爲1的內容來做爲測試數據,若是咱們須要把 excel 中全部 sheet 中的數據都做爲測試數據來供 pytest 參數化的話,那麼就能夠實例化 CaseDataAllSheets 類。

4. 測試執行

最後,咱們在 main.py 中運行 pytest 主程序

if __name__ == '__main__':
    import pytest
    pytest.main(['-s', '-q', '-vv', '--html=./report/report.html', '--self-contained-html'])
複製代碼

使用一個 report 插件來自動產生測試報告。

至此,咱們之後只須要編寫易於操做的 excel,而幾乎不須要動任何 Python 代碼,就能完成一次接口自動化測試了。固然,編寫好的 excel 要放到 data 文件夾下哦!

相關文章
相關標籤/搜索