python+requests接口自動(dòng)化框架

為什么要做接口自動(dòng)化框架

1、業(yè)務(wù)與配置的分離

2端姚、數(shù)據(jù)與程序的分離晕粪;數(shù)據(jù)的變更不影響程序

3、有日志功能渐裸,實(shí)現(xiàn)無(wú)人值守

4巫湘、自動(dòng)發(fā)送測(cè)試報(bào)告

5、不懂編程的測(cè)試人員也可以進(jìn)行測(cè)試

正常接口測(cè)試的流程是什么昏鹃?

確定接口測(cè)試使用的工具----->配置需要的接口參數(shù)----->進(jìn)行測(cè)試----->檢查測(cè)試結(jié)果----->生成測(cè)試報(bào)告

測(cè)試的工具:python+requests

接口測(cè)試用例:excel

一尚氛、接口框架如下:

1、action包:用來(lái)存放關(guān)鍵字函數(shù)

2洞渤、config包:用來(lái)存放配置文件

3阅嘶、TestData:用來(lái)存放測(cè)試數(shù)據(jù),excel表

4载迄、Log包:用來(lái)存放日志文件

5讯柔、utils包:用來(lái)存放公共的類

6、運(yùn)行主程序interface_auto_test.py

7护昧、Readme.txt:告訴團(tuán)隊(duì)組員使用改框架需要注意的地方

二魂迄、接口的數(shù)據(jù)規(guī)范設(shè)計(jì)---Case設(shè)計(jì)

一個(gè)sheet對(duì)應(yīng)數(shù)據(jù)庫(kù)里面一張表

APIsheet存放
編號(hào);從1開始
接口的名稱(APIName);
請(qǐng)求的url(RequestUrl);
請(qǐng)求的方法(RequestMethod)呀潭;
傳參的方式(paramsType):post/get請(qǐng)求方法不一樣
用例說(shuō)明(APITestCase)
是否執(zhí)行(Active)部分接口已測(cè)通焚廊,下次不用測(cè)試如贷,直接把這里設(shè)置成N荧缘,跳過(guò)此接口

post與get的區(qū)別

查看post詳情

post請(qǐng)求參數(shù)一般是json串诗力,參數(shù)放在from表單里面烙心;參數(shù)一般不可見稿械,相對(duì)來(lái)說(shuō)安全性高些

查看get詳情

get請(qǐng)求參數(shù)一般直接放在url里面

2.1注冊(cè)接口用例

RequestData:請(qǐng)求的數(shù)據(jù)
(開發(fā)制定的傳參方式)
RelyData:數(shù)據(jù)依賴
ResponseCode:響應(yīng)code
ResponseData:響應(yīng)數(shù)據(jù)
DataStore:存儲(chǔ)的依賴數(shù)據(jù)选泻;如果存在數(shù)據(jù)庫(kù)里面,在表里增加一個(gè)字段用來(lái)存依賴的數(shù)據(jù)
(存儲(chǔ)的方式是編寫接口自動(dòng)化的人員來(lái)設(shè)定的存儲(chǔ)方式)
CheckPoint:檢查點(diǎn)
Active:是否執(zhí)行
Status:執(zhí)行用例的狀態(tài)美莫,方便查看用例是否執(zhí)行成功
ErrorInfo:case運(yùn)行失敗页眯,失敗的錯(cuò)誤信息;eg:是也本身的原因還是case設(shè)置失敗厢呵,還是其他原因

2.2登錄接口用例

RequestData:請(qǐng)求的數(shù)據(jù)
(開發(fā)制定的傳參方式)
RelyData:數(shù)據(jù)依賴
(存儲(chǔ)的方式是編寫接口自動(dòng)化的人員來(lái)設(shè)定的存儲(chǔ)方式)
ResponseCode:響應(yīng)code
ResponseData:響應(yīng)數(shù)據(jù)
DataStore:存儲(chǔ)的依賴數(shù)據(jù)窝撵;如果存在數(shù)據(jù)庫(kù)里面,在表里增加一個(gè)字段用來(lái)存依賴的數(shù)據(jù)
(存儲(chǔ)的方式是編寫接口自動(dòng)化的人員來(lái)設(shè)定的存儲(chǔ)方式)
CheckPoint:檢查點(diǎn)
Active:是否執(zhí)行
Status:執(zhí)行用例的狀態(tài)襟铭,方便查看用例是否執(zhí)行成功
ErrorInfo:case運(yùn)行失敗碌奉,失敗的錯(cuò)誤信息;eg:是也本身的原因還是case設(shè)置失敗寒砖,還是其他原因

重點(diǎn)說(shuō)明下RelyData:數(shù)據(jù)依賴
采取的是字典:key:value來(lái)存儲(chǔ)數(shù)據(jù)格式赐劣;

{"request":{"username":"register->1","password":"register->1"},"response":{"code":"register->1"}}

格式化之后:

{
    "request":{
        "username":"register->1",
        "password":"register->1"
    },
    "response":{
        "code":"register->1"
    }
}

三、創(chuàng)建utils包:用來(lái)存放公共的類

3.1 ParseExcel.py 操作封裝excel的類(ParseExcel.py)
#encoding=utf-8
import openpyxl
from openpyxl.styles import Border, Side, Font
import time

class ParseExcel(object):

    def __init__(self):
        self.workbook = None
        self.excelFile = None
        self.font = Font(color = None) # 設(shè)置字體的顏色
        # 顏色對(duì)應(yīng)的RGB值
        self.RGBDict = {'red': 'FFFF3030', 'green': 'FF008B00'}

    def loadWorkBook(self, excelPathAndName):
        # 將excel文件加載到內(nèi)存哩都,并獲取其workbook對(duì)象
        try:
            self.workbook = openpyxl.load_workbook(excelPathAndName)
        except Exception as err:
            raise err
        self.excelFile = excelPathAndName
        return self.workbook

    def getSheetByName(self, sheetName):
        # 根據(jù)sheet名獲取該sheet對(duì)象
        try:
            # sheet = self.workbook.get_sheet_by_name(sheetName)
            sheet = self.workbook[sheetName]
            return sheet
        except Exception as err:
            raise err

    def getSheetByIndex(self, sheetIndex):
        # 根據(jù)sheet的索引號(hào)獲取該sheet對(duì)象
        try:
            # sheetname = self.workbook.get_sheet_names()[sheetIndex]
            sheetname = self.workbook.sheetnames[sheetIndex]
        except Exception as err:
            raise err
        # sheet = self.workbook.get_sheet_by_name(sheetname)
        sheet = self.workbook[sheetname]
        return sheet

    def getRowsNumber(self, sheet):
        # 獲取sheet中有數(shù)據(jù)區(qū)域的結(jié)束行號(hào)
        return sheet.max_row

    def getColsNumber(self, sheet):
        # 獲取sheet中有數(shù)據(jù)區(qū)域的結(jié)束列號(hào)
        return sheet.max_column

    def getStartRowNumber(self, sheet):
        # 獲取sheet中有數(shù)據(jù)區(qū)域的開始的行號(hào)
        return sheet.min_row

    def getStartColNumber(self, sheet):
        # 獲取sheet中有數(shù)據(jù)區(qū)域的開始的列號(hào)
        return sheet.min_column

    def getRow(self, sheet, rowNo):
        # 獲取sheet中某一行,返回的是這一行所有的數(shù)據(jù)內(nèi)容組成的tuple魁兼,
        # 下標(biāo)從1開始,sheet.rows[1]表示第一行
        try:
            rows = []
            for row in sheet.iter_rows():
                rows.append(row)
            return rows[rowNo - 1]
        except Exception as err:
            raise err

    def getColumn(self, sheet, colNo):
        # 獲取sheet中某一列,返回的是這一列所有的數(shù)據(jù)內(nèi)容組成tuple漠嵌,
        # 下標(biāo)從1開始咐汞,sheet.columns[1]表示第一列
        try:
            cols = []
            for col in sheet.iter_cols():
                cols.append(col)
            return cols[colNo - 1]
        except Exception as err:
            raise err

    def getCellOfValue(self, sheet, coordinate = None,
                       rowNo = None, colsNo = None):
        # 根據(jù)單元格所在的位置索引獲取該單元格中的值,下標(biāo)從1開始,
        # sheet.cell(row = 1, column = 1).value,
        # 表示excel中第一行第一列的值
        if coordinate != None:
            try:
                return sheet[coordinate]
            except Exception as err:
                raise err
        elif coordinate is None and rowNo is not None and \
                        colsNo is not None:
            try:
                return sheet.cell(row = rowNo, column = colsNo).value
            except Exception as err:
                raise err
        else:
            raise Exception("Insufficient Coordinates of cell !")

    def getCellOfObject(self, sheet, coordinate = None,
                        rowNo = None, colsNo = None):
        # 獲取某個(gè)單元格的對(duì)象儒鹿,可以根據(jù)單元格所在位置的數(shù)字索引化撕,
        # 也可以直接根據(jù)excel中單元格的編碼及坐標(biāo)
        # 如getCellObject(sheet, coordinate = 'A1') or
        # getCellObject(sheet, rowNo = 1, colsNo = 2)
        if coordinate != None:
            try:
                # return sheet.cell(coordinate = coordinate)
                return sheet[coordinate]
            except Exception as err:
                raise err
        elif coordinate == None and rowNo is not None and \
                        colsNo is not None:
            try:
                return sheet.cell(row = rowNo,column = colsNo)
            except Exception as err:
                raise err
        else:
            raise Exception("Insufficient Coordinates of cell !")

    def writeCell(self, sheet, content, coordinate = None,
        rowNo = None, colsNo = None, style = None):
        #根據(jù)單元格在excel中的編碼坐標(biāo)或者數(shù)字索引坐標(biāo)向單元格中寫入數(shù)據(jù),
        # 下標(biāo)從1開始约炎,參style表示字體的顏色的名字,比如red植阴,green
        if coordinate is not None:
            try:
                # sheet.cell(coordinate = coordinate).value = content
                sheet[coordinate] = content
                if style is not None:
                    sheet[coordinate].\
                        font = Font(color = self.RGBDict[style])
                self.workbook.save(self.excelFile)
            except Exception as e:
                raise e
        elif coordinate == None and rowNo is not None and \
                        colsNo is not None:
            try:
                sheet.cell(row = rowNo,column = colsNo).value = content
                if style:
                    sheet.cell(row = rowNo,column = colsNo).\
                        font = Font(color = self.RGBDict[style])
                self.workbook.save(self.excelFile)
            except Exception as e:
                raise e
        else:
            raise Exception("Insufficient Coordinates of cell !")

    def writeCellCurrentTime(self, sheet, coordinate = None,
                rowNo = None, colsNo = None):
        # 寫入當(dāng)前的時(shí)間,下標(biāo)從1開始
        now = int(time.time())  #顯示為時(shí)間戳
        timeArray = time.localtime(now)
        currentTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
        if coordinate is not None:
            try:
                sheet.cell(coordinate = coordinate).value = currentTime
                self.workbook.save(self.excelFile)
            except Exception as e:
                raise e
        elif coordinate == None and rowNo is not None \
                and colsNo is not None:
            try:
                sheet.cell(row = rowNo, column = colsNo
                        ).value = currentTime
                self.workbook.save(self.excelFile)
            except Exception as e:
                raise e
        else:
            raise Exception("Insufficient Coordinates of cell !")

if __name__ == '__main__':
    # 測(cè)試代碼
    pe = ParseExcel()
    pe.loadWorkBook(r'D:\ProgramSourceCode\Python Source Code\WorkSpace\InterfaceFrame2018\inter_test_data.xlsx')
    sheetObj = pe.getSheetByName(u"API")
    print("通過(guò)名稱獲取sheet對(duì)象的名字:", sheetObj.title)
    # print help(sheetObj.rows)
    print("通過(guò)index序號(hào)獲取sheet對(duì)象的名字:", pe.getSheetByIndex(0).title)
    sheet = pe.getSheetByIndex(0)
    print(type(sheet))
    print(pe.getRowsNumber(sheet))  #獲取最大行號(hào)
    print(pe.getColsNumber(sheet))  #獲取最大列號(hào)
    rows = pe.getRow(sheet, 1)  #獲取第一行
    for i in rows:
        print(i.value)
    # # 獲取第一行第一列單元格內(nèi)容
    # print pe.getCellOfValue(sheet, rowNo = 1, colsNo = 1)
    # pe.writeCell(sheet, u'我愛祖國(guó)', rowNo = 10, colsNo = 10)
    # pe.writeCellCurrentTime(sheet, rowNo = 10, colsNo = 11)
3.2 封裝get/post請(qǐng)求(HttpClient.py)
import requests
import json

class HttpClient(object):
    def __init__(self):
        pass

    def request(self, requestMethod, requestUrl, paramsType,
                requestData, headers =None, **kwargs):
        if requestMethod == "post":
            print("---", requestData, type(requestData))
            if paramsType == "form":
                response = self.__post(url = requestUrl, data = json.dumps(eval(requestData)),
                                  headers = headers, **kwargs)
                return response
            elif paramsType == "json":
                response = self.__post(url = requestUrl, json = json.dumps(eval(requestData)),
                                  headers = headers, **kwargs)
                return response
        elif requestMethod == "get":
            request_url = requestUrl
            if paramsType == "url":
                request_url = "%s%s" %(requestUrl, requestData)
            response = self.__get(url = request_url, params = requestData, **kwargs)
            return response

    def __post(self, url, data = None, json = None, headers=None,**kwargs):
        print("----")
        response = requests.post(url=url, data = data, json=json, headers=headers)
        return response

    def __get(self, url, params = None, **kwargs):
        response = requests.get(url, params = params, **kwargs)
        return response

if __name__ == "__main__":
    hc = HttpClient()
    res = hc.request("get", "http://39.106.41.11:8080/getBlogContent/", "url",'2')
    print(res.json())
3.3 封裝MD5(md5_encrypt)
import hashlib

def md5_encrypt(text):
    m5 = hashlib.md5()
    m5.update(text.encode("utf-8"))
    value = m5.hexdigest()
    return value

if __name__ == "__main__":
    print(md5_encrypt("sfwe"))
3.4 封裝Log
import logging
import logging.config
from config.public_data import baseDir

# 讀取日志配置文件
logging.config.fileConfig(baseDir + "\config\Logger.conf")
# 選擇一個(gè)日志格式
logger = logging.getLogger("example02")#或者example01

def debug(message):
    # 定義dubug級(jí)別日志打印方法
    logger.debug(message)

def info(message):
    # 定義info級(jí)別日志打印方法
    logger.info(message)

def warning(message):
    # 定義warning級(jí)別日志打印方法
    logger.warning(message)
3.5 封裝發(fā)送Email類
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from ProjVar.var import *

import os
import smtplib
from email import encoders
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.utils import formataddr

def send_mail():
    mail_host="smtp.qq.com"  #設(shè)置服務(wù)器
    mail_user="xiangxiang"    #用戶名
    mail_pass="cmxx"   #口令
    sender = 'cm2019@126.com'
    receivers = ['672014873@qq.com',"cm2019@126.com"] # 接收郵件圾浅,可設(shè)置為你的QQ郵箱或者其他郵箱
    # 創(chuàng)建一個(gè)帶附件的實(shí)例
    message = MIMEMultipart()
    message['From'] = formataddr(["自動(dòng)化測(cè)試", "cm2019@126.com"])
    message['To'] = ','.join(receivers)
    subject = '自動(dòng)化測(cè)試執(zhí)行報(bào)告'
    message['Subject'] = Header(subject, 'utf-8')
    message["Accept-Language"]="zh-CN"
    message["Accept-Charset"]="ISO-8859-1,utf-8,gbk"
    # 郵件正文內(nèi)容
    message.attach(MIMEText('最新執(zhí)行的自動(dòng)化測(cè)試報(bào)告墙贱,請(qǐng)參閱附件內(nèi)容!', 'plain', 'utf-8'))

    # 構(gòu)造附件1贱傀,傳送測(cè)試結(jié)果的excel文件
    att = MIMEBase('application', 'octet-stream')
    att.set_payload(open(ProjDirPath+"\\testdata\\testdata.xlsx", 'rb').read())
    att.add_header('Content-Disposition', 'attachment', filename=('gbk', '', "自動(dòng)化測(cè)試報(bào)告.xlsx"))
    encoders.encode_base64(att)
    message.attach(att)
    """
    # 構(gòu)造附件2,傳送當(dāng)前目錄下的 runoob.txt 文件
    att2 = MIMEText(open('e:\\a.py','rb').read(), 'base64', 'utf-8')
    att2["Content-Type"] = 'application/octet-stream'
    att2["Content-Disposition"] = 'attachment; filename="a.py"'
    message.attach(att2)
    """
    try:
        smtpObj = smtplib.SMTP(mail_host)
        smtpObj.login(mail_user, mail_pass)
        smtpObj.sendmail(sender, receivers, message.as_string())
        print("郵件發(fā)送成功")
    except smtplib.SMTPException as e:
        print("Error: 無(wú)法發(fā)送郵件", e)

if __name__ == "__main__":
    send_mail()

四伊脓、 創(chuàng)建config包 用來(lái)存放公共的參數(shù)府寒、配置文件魁衙、長(zhǎng)時(shí)間不變的變量值

創(chuàng)建public_data.py

import os
# 整個(gè)項(xiàng)目的根目錄絕對(duì)路勁
baseDir = os.path.dirname(os.path.dirname(__file__))

# 獲取測(cè)試數(shù)據(jù)文件的絕對(duì)路徑
file_path = baseDir + "/TestData/inter_test_data.xlsx"

API_apiName = 2
API_requestUrl = 3
API_requestMothod = 4
API_paramsType = 5
API_apiTestCaseFileName = 6
API_active = 7

CASE_requestData = 1
CASE_relyData = 2
CASE_responseCode = 3
CASE_responseData = 4
CASE_dataStore = 5
CASE_checkPoint = 6
CASE_active = 7
CASE_status = 8
CASE_errorInfo = 9

# 存儲(chǔ)請(qǐng)求參數(shù)里面依賴的數(shù)據(jù)
REQUEST_DATA = {}

# 存儲(chǔ)響應(yīng)對(duì)象中的依賴數(shù)據(jù)
RESPONSE_DATA = {}

if __name__=="__main__":
    print(file_path)
    print(baseDir)
五、創(chuàng)建TestData目錄株搔,用來(lái)存放測(cè)試文件

inter_test_data.xlsx

六剖淀、創(chuàng)建action包,用來(lái)存放關(guān)鍵字函數(shù)
6.1 解決數(shù)據(jù)依賴 (GetRely.py)
from config.public_data import REQUEST_DATA, RESPONSE_DATA
from utils.md5_encrypt import md5_encrypt

REQUEST_DATA = {"用戶注冊(cè)":{"1":{"username":"zhangsan", "password":"dfsdf23"},
                        "headers":{"cookie":"asdfwerw"}}}
RESPONSE_DATA = {"用戶注冊(cè)":{"1":{"code":"00"}, "headers":{"age":2342}}}

class GetRely(object):
    def __init__(self):
        pass

    @classmethod
    def get(self, dataSource, relyData, headSource = {}):
        print(type(dataSource))
        print(dataSource)
        data = dataSource.copy()
        for key, value in relyData.items():
            if key == "request":
                #說(shuō)明應(yīng)該去REQUEST_DATA中獲取
                for k, v in value.items():
                    interfaceName, case_idx = v.split("->")
                    val = REQUEST_DATA[interfaceName][case_idx][k]
                    if k == "password":
                        data[k] = md5_encrypt(val)
                    else:
                        data[k] = val
            elif key == "response":
                # 應(yīng)該去RESPONSE_DATA中獲取
                for k, v in value.items():
                    interfaceName, case_idx = v.split("->")
                    data[k] = RESPONSE_DATA[interfaceName][case_idx][k]
            elif key == "headers":
                if headSource:
                    for key, value in value.items():
                        if key == "request":
                            for k, v in value.items():
                                for i in v:
                                    headSource[i] = REQUEST_DATA[k]["headers"][i]
                        elif key == "response":
                            for i, val in value.items():
                                for j in val:
                                    headSource[j] = RESPONSE_DATA[i]["headers"][j]
        return "%s" %data

if __name__ == "__main__":
    s = {"username": "", "password": "","code":""}
    h = {"cookie":"123", "age":332}
    rely = {"request": {"username": "用戶注冊(cè)->1", "password": "用戶注冊(cè)->1"},
            "response":{"code":"用戶注冊(cè)->1"},
            "headers":{"request":{"用戶注冊(cè)":["cookie"]},"response":{"用戶注冊(cè)":["age"]}}
            }
    print(GetRely.get(s, rely, h))
6.2 解決數(shù)據(jù)存儲(chǔ)(RelyDataStore.py)
from config.public_data import RESPONSE_DATA, REQUEST_DATA

class RelyDataStore(object):
    def __init__(self):
        pass

    @classmethod
    def do(cls, storePoint, apiName, caseId, request_source = {}, response_source = {}, req_headers={}, res_headers = {}):
        for key, value in storePoint.items():
            if key == "request":
                # 說(shuō)明需要存儲(chǔ)的依賴數(shù)據(jù)來(lái)自請(qǐng)求參數(shù),應(yīng)該將數(shù)據(jù)存儲(chǔ)到REQUEST_DATA
                for i in value:
                    if i in request_source:
                        val = request_source[i]
                        if apiName not in REQUEST_DATA:
                            # 說(shuō)明存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)還未生成纤房,需要指明數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)
                            REQUEST_DATA[apiName]={str(caseId): {i: val}}
                        else:
                            #說(shuō)明存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)中最外層結(jié)構(gòu)已存在
                            if str(caseId) in REQUEST_DATA[apiName]:
                                REQUEST_DATA[apiName][str(caseId)][i] = val
                            else:
                                # 說(shuō)明內(nèi)層結(jié)構(gòu)不完整纵隔,需要指明完整的結(jié)構(gòu)
                                REQUEST_DATA[apiName][str(caseId)] = {i: val}
                    else:
                        print("請(qǐng)求參數(shù)中不存在字段" + i)
            elif key == "response":
                #說(shuō)明需要存儲(chǔ)的依賴數(shù)據(jù)來(lái)自接口的響應(yīng)body,應(yīng)該將數(shù)據(jù)存儲(chǔ)到RESPONSE_DATA
                for j in value:
                    if j in response_source:
                        val = response_source[j]
                        if apiName not in RESPONSE_DATA:
                            # 說(shuō)明存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)還未生成,需要指明數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)
                            RESPONSE_DATA[apiName]={str(caseId): {j: val}}
                        else:
                            #說(shuō)明存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)中最外層結(jié)構(gòu)已存在
                            if str(caseId) in RESPONSE_DATA[apiName]:
                                RESPONSE_DATA[apiName][str(caseId)][j] = val
                            else:
                                # 說(shuō)明內(nèi)層結(jié)構(gòu)不完整炮姨,需要指明完整的結(jié)構(gòu)
                                RESPONSE_DATA[apiName][str(caseId)] = {j: val}
                    else:
                        print("接口的響應(yīng)body中不存在字段" + j)
            elif key == "headers":
                for k, v in value.items():
                    if k == "request":
                        # 說(shuō)明需要往REQUEST_DATA變量中寫入存儲(chǔ)數(shù)據(jù)
                        for item in v:
                            if item in req_headers:
                                header = req_headers[item]
                                if "headers" in REQUEST_DATA[apiName]:
                                    REQUEST_DATA[apiName]["headers"][item] = header
                                else:
                                    REQUEST_DATA[apiName]["headers"] = {item: header}
                    elif k == "response":
                        # 說(shuō)明需要往RESPONSE_DATA變量中寫入存儲(chǔ)數(shù)據(jù)
                        for it in v:
                            if it in res_headers:
                                header = res_headers[it]
                                if "headers" in RESPONSE_DATA[apiName]:
                                    RESPONSE_DATA[apiName]["headers"][it] = header
                                else:
                                    RESPONSE_DATA[apiName]["headers"] = {item: header}
        print(REQUEST_DATA)
        print(RESPONSE_DATA)

if __name__ == "__main__":
    r = {"username": "srwcx01", "password": "wcx123wac1", "email": "wcx@qq.com"}
    req_h = {"cookie":"csdfw23"}
    res_h = {"age":597232}
    s = {"request": ["username", "password"], "response": ["userid"],"headers":{"request":["cookie"],
        "response":["age"]}}
    res = {"userid": 12, "code": "00"}
    RelyDataStore.do(s, "register", 1, r, res, req_headers=req_h, res_headers=res_h)
    print(REQUEST_DATA)
    print(RESPONSE_DATA)
6.3 校驗(yàn)數(shù)據(jù)結(jié)果(CheckResult.py)
import re

class CheckResult(object):
    def __init__(self):
        pass

    @classmethod
    def check(self, responseObj, checkPoint):
        responseBody = responseObj.json()
        # responseBody = {"code": "", "userid": 12, "id": "12"}
        errorKey = {}
        for key, value in checkPoint.items():
            if key in responseBody:
                if isinstance(value, (str, int)):
                    # 等值校驗(yàn)
                    if responseBody[key] != value:
                        errorKey[key] = responseBody[key]
                elif isinstance(value, dict):
                    sourceData = responseBody[key]
                    if "value" in value:
                        # 模糊匹配校驗(yàn)
                        regStr = value["value"]
                        rg = re.match(regStr, "%s" %sourceData)
                        if not rg:
                            errorKey[key] = sourceData
                    elif "type" in value:
                        # 數(shù)據(jù)類型校驗(yàn)
                        typeS = value["type"]
                        if typeS == "N":
                            # 說(shuō)明是整形校驗(yàn)
                            if not isinstance(sourceData, int):
                                errorKey[key] = sourceData
            else:
                errorKey[key] = "[%s] not exist" %key
        return errorKey

if __name__ == "__main__":
    r = {"code": "00", "userid": 12, "id": 12}
    c = {"code": "00", "userid": {"type": "N"}, "id": {"value": "\d+"}}
    print(CheckResult.check(r, c))
6.4 往excel里面寫結(jié)果
from config.public_data import *

def write_result(wbObj, sheetObj, responseData, errorKey, rowNum):
    try:
        # 寫響應(yīng)body
        wbObj.writeCell(sheetObj, content="%s" %responseData,
                        rowNo = rowNum, colsNo=CASE_responseData)
        # 寫校驗(yàn)結(jié)果狀態(tài)及錯(cuò)誤信息
        if errorKey:
            wbObj.writeCell(sheetObj, content="%s" %errorKey,
                        rowNo=rowNum, colsNo=CASE_errorInfo)
            wbObj.writeCell(sheetObj, content="faild",
                            rowNo=rowNum, colsNo=CASE_status, style="red")
        else:
            wbObj.writeCell(sheetObj, content="pass",
                            rowNo=rowNum, colsNo=CASE_status, style="green")
    except Exception as err:
        raise err

如果對(duì)軟件測(cè)試捌刮、接口測(cè)試、自動(dòng)化測(cè)試舒岸、面試經(jīng)驗(yàn)交流绅作。感興趣可以加軟件測(cè)試交流:1085991341,還會(huì)有同行一起技術(shù)交流蛾派。

七俄认、創(chuàng)建Log目錄用來(lái)存放日志
八、主函數(shù)
#encoding=utf-8
import requests
import json
from action.get_rely import GetRely
from config.public_data import *
from utils.ParseExcel import ParseExcel
from utils.HttpClient import HttpClient
from action.data_store import RelyDataStore
from action.check_result import CheckResult
from action.write_result import write_result
from utils.Log import *

def main():
    parseE = ParseExcel()
    parseE.loadWorkBook(file_path)
    sheetObj = parseE.getSheetByName("API")
    activeList = parseE.getColumn(sheetObj, API_active)
    for idx, cell in enumerate(activeList[1:], 2):
        if cell.value == "y":
            #需要被執(zhí)行
            RowObj = parseE.getRow(sheetObj, idx)
            apiName = RowObj[API_apiName -1].value
            requestUrl = RowObj[API_requestUrl - 1].value
            requestMethod = RowObj[API_requestMothod - 1].value
            paramsType = RowObj[API_paramsType - 1].value
            apiTestCaseFileName = RowObj[API_apiTestCaseFileName - 1].value

            # 下一步讀取用例sheet表洪乍,準(zhǔn)備執(zhí)行測(cè)試用例
            caseSheetObj = parseE.getSheetByName(apiTestCaseFileName)
            caseActiveObj = parseE.getColumn(caseSheetObj, CASE_active)
            for c_idx, col in enumerate(caseActiveObj[1:], 2):
                if col.value == "y":
                    #需要執(zhí)行的用例
                    caseRowObj = parseE.getRow(caseSheetObj, c_idx)
                    requestData = caseRowObj[CASE_requestData - 1].value
                    relyData = caseRowObj[CASE_relyData - 1].value
                    responseCode = caseRowObj[CASE_responseCode - 1].value
                    responseData = caseRowObj[CASE_responseData - 1].value
                    dataStore = caseRowObj[CASE_dataStore -1].value
                    checkPoint = caseRowObj[CASE_checkPoint - 1].value

                    #發(fā)送接口請(qǐng)求之前需要做一下數(shù)據(jù)依賴的處理
                    if relyData:
                        logging.info("處理第%s個(gè)接口的第%s條用例的數(shù)據(jù)依賴眯杏!")
                        requestData = GetRely.get(eval(requestData), eval(relyData))
                    httpC = HttpClient()
                    response = httpC.request(requestMethod=requestMethod,
                                             requestData=requestData,
                                             requestUrl=requestUrl,
                                             paramsType=paramsType
                                             )
                    # 獲取到響應(yīng)結(jié)果后,接下來(lái)進(jìn)行數(shù)據(jù)依賴存儲(chǔ)邏輯實(shí)現(xiàn)
                    if response.status_code == 200:
                        responseData = response.json()
                        # 進(jìn)行依賴數(shù)據(jù)存儲(chǔ)
                        if dataStore:
                            RelyDataStore.do(eval(dataStore), apiName, c_idx - 1, eval(requestData), responseData)
                        # 接下來(lái)就是校驗(yàn)結(jié)果
                        else:
                            logging.info("接口【%s】的第【%s】條用例壳澳,不需要進(jìn)行依賴數(shù)據(jù)存儲(chǔ)岂贩!" %(apiName, c_idx))
                        if checkPoint:
                            errorKey = CheckResult.check(response, eval(checkPoint))
                            write_result(parseE, caseSheetObj, responseData, errorKey, c_idx)
                    else:
                        logging.info("接口【%s】的第【%s】條用例,執(zhí)行失敗钾埂,接口協(xié)議code非200河闰!" %(apiName, c_idx))
                else:
                    logging.info("第%s個(gè)接口的第%s條用例,被忽略執(zhí)行褥紫!" %(idx -1, c_idx-1))
        else:
            logging.info("第%s行的接口被忽略執(zhí)行姜性!" %(idx -1))

if __name__=="__main__":
    main()

框架待完善,請(qǐng)大家多多指教~
以上內(nèi)容希望對(duì)你有幫助髓考,有被幫助到的朋友歡迎點(diǎn)贊部念,評(píng)論。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末氨菇,一起剝皮案震驚了整個(gè)濱河市儡炼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌查蓉,老刑警劉巖乌询,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異豌研,居然都是意外死亡妹田,警方通過(guò)查閱死者的電腦和手機(jī)唬党,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鬼佣,“玉大人驶拱,你說(shuō)我怎么就攤上這事【е裕” “怎么了蓝纲?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)晌纫。 經(jīng)常有香客問(wèn)我税迷,道長(zhǎng),這世上最難降的妖魔是什么缸匪? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任翁狐,我火速辦了婚禮,結(jié)果婚禮上凌蔬,老公的妹妹穿的比我還像新娘露懒。我一直安慰自己,他們只是感情好砂心,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布懈词。 她就那樣靜靜地躺著,像睡著了一般辩诞。 火紅的嫁衣襯著肌膚如雪坎弯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天译暂,我揣著相機(jī)與錄音抠忘,去河邊找鬼。 笑死外永,一個(gè)胖子當(dāng)著我的面吹牛崎脉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伯顶,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼囚灼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了祭衩?” 一聲冷哼從身側(cè)響起灶体,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掐暮,沒想到半個(gè)月后蝎抽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡路克,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年织中,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锥涕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狭吼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出殖妇,到底是詐尸還是另有隱情刁笙,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布谦趣,位于F島的核電站疲吸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏前鹅。R本人自食惡果不足惜摘悴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舰绘。 院中可真熱鬧蹂喻,春花似錦、人聲如沸捂寿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)秦陋。三九已至蔓彩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驳概,已是汗流浹背赤嚼。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顺又,地道東北人更卒。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像待榔,于是被迫代替她去往敵國(guó)和親逞壁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345