一.介面自動化需求分析:
介面自動化測試用例:
1.用例寫在excel表格裡面,定義函式獲取excel表格中資料並加入到用例列表
中進行返回
a.Excel表格中的資料只有url/請求方式、請求引數、headers、是否json、
預期結果才是有效資料
b.請求引數定義格式是"xxx=123,sss>456,phone=<phone>"這種的,需要
轉換成字典的形式,並且<phone>需要進行引數化
c.headers請求頭需要轉換成字典
2.用例讀取完成後,需要進行傳送請求,需要構造一個請求的函式,請求成功後返
回請求結果json格式和字串格式的結果
3.拿到請求結果後,需要對預期結果和實際結果進行比對,只要有一條資料不透過
就失敗
4.最後進行串聯整個流程
a.先讀取測試用例檔案大,獲取所有測試用例excel檔案
b.執行測試用例,
先根據excel檔案獲取到所有測試用例;
再迴圈測試用例並進行介面請求,獲取到介面返回的結果;
然後根據結果返回的實際結果與預期結果進行比較,並把執行總條數、成功數、狀態、失敗原因放在列表中儲存在response_list中;
最後把執行結果寫入到excel表格中
c.最後傳送介面測試用例執行結果報告郵件
二.進行介面自動化專案程式定義分類:
1.建立一個AutoTestProject資料夾後並在該資料夾下分別建立bin、case、config、lib、logs、report資料夾,bin目錄做完程式的入口目錄並在下面建立start.py檔案;case目錄用來存放結果自動化測試用例;config用來存放配置檔案及自動化測試用例模板檔案;lib資料夾下存放read_case.py、request.py、parse_response.py、utils.py等資料夾;logs資料夾用來存放日誌的;report資料夾用來存放測試報告的
2.在case檔案目錄下存放測試用例檔案,測試用例以test開頭和xls或xlsx結尾
3.config資料夾下定義配置相關內容及模板檔案
setting.py檔案
import os import random import string import faker # 傳送郵件 EMAIL_INFO={ "user":"13424210282@163.com", "password":"HLXWWGTUWGNAAAEA", "host":"smtp.163.com", } # 郵件接收人 T0=["974219141@qq.com"] # 郵件抄送人 CC=['751462075@qq.com'] # 介面請求url地址配置 host_map={ "fat":"http://127.0.0.1:8787", "uat":"", "pro":"" } default="fat" HOST=host_map.get(default) # 自動化專案父目錄 BASE_PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 測試用例目錄 CASE_PATH=os.path.join(BASE_PATH,'case') # 日誌檔案目錄 LOG_PATH=os.path.join(BASE_PATH,"logs",'atp.log') # 測試報告目錄 REPORT_PATH=os.path.join(BASE_PATH,'report') # faker.Faker生成資料進行引數化 f=faker.Faker(locale="zh-CN") """ 進行引數化的字典,後續有欄位數值需要進行引數化的時候只需要在這裡進行維護 """ def getPassword(): """ 生成隨機密碼的 :return: """ a=random.sample(string.digits,2) b=random.sample(string.ascii_uppercase,2) c=random.sample(string.ascii_lowercase,2) d=random.sample("@#$%^&*",2) s=a+b+c+d # 資料清洗或打亂 random.shuffle(s) return "".join(s) func_map={ "<phone>":f.phone_number, "<id_card>":f.ssn, "<email>":f.email, "<name>":f.name, "<addr>":f.address, "<password>":getPassword } import nnlog LOG=nnlog.Logger(LOG_PATH)
4.lib檔案下定義utils.py進行發郵件、獲取字典中的值、寫報告功能;read_case.py定義獲取用例列表;request.py定義傳送請求;parse_response.py定義結果比對
A.utils.py
from xlutils import copy import os import random import string import time import traceback import yagmail from config.setting import REPORT_PATH,LOG,EMAIL_INFO,T0,CC import jsonpath import xlrd def get_value(d,k): # 透過jsonpath.jsonpath來取字典中key所對應的值 result=jsonpath.jsonpath(d,'$..%s'%k) # 如果取到了返回第一個值,否則返回空字串 if result: return result[0] return '' """ result_list=[ [{"code":0,"msg":"成功"},"透過"], [{"code":0,"msg":"成功"},"失敗"], ] """ def write_excel(file_name,result_list): """ result_list存放用例執行結果["引數","執行結果","原因","狀態"] :param file_name: :param result_list: :return: """ LOG.debug("現在開始寫報告了:檔名是%s,內容是%s"%(file_name,result_list)) book=xlrd.open_workbook(file_name) newbook=copy.copy(book) try: sheet=newbook.get_sheet(0) except Exception as e: print("無法開啟excel=============",file_name) for row,result in enumerate(result_list,1): for col ,value in enumerate(result[1:],7): sheet.write(row,col,value) sheet.write(row,3,result[0]) file_name=os.path.split(file_name)[-1].replace("xlsx","xls") new_file_name=time.strftime("%Y%m%d%H%M%S")+"_"+file_name case_file=os.path.join(REPORT_PATH,new_file_name) newbook.save(case_file) LOG.debug("報告生成完成,檔名是%s"%(case_file)) return case_file def send_email(all_count,pass_count,file_name): LOG.debug("開始傳送報告了,檔名是%s"%file_name) content=''' 各位好: 本次介面自動化測試結果如下,總共執行%s條用例,透過%s條,失敗【%s】條。 詳情請檢視附件。 '''%(all_count,pass_count,(all_count-pass_count)) subject='%s-介面測試報告'%time.strftime("%Y-%m-%d %H%M%S") email=yagmail.SMTP(**EMAIL_INFO,encoding="GBK") try: email.send(to=T0,cc=CC,subject=subject,contents=content,attachments=file_name) except Exception as e: LOG.error("傳送報告失敗,具體失敗原因是%s"%traceback.format_exc()) else: LOG.debug("報告傳送成功")
B.read_case.py
import nnlog import traceback import xlrd from config.setting import LOG_PATH,func_map import jsonpath log=nnlog.Logger(LOG_PATH) # "$..key"是進行模糊查詢 # jsonpath.jsonpath(d,"$..key") # faker模板的使用 class ParamDeal(): def read_excel(self,file_path): # case_list存放測試用例 case_list=[] try: # 開啟excel book=xlrd.open_workbook(file_path) sheet=book.sheet_by_index(0) # 從 for row in range(1,sheet.nrows): # 只取有用的那幾行 line=sheet.row_values(row)[1:7] # 把引數先進行替換 line[2]=self.replace_param(line[2]) # 將header轉換成字典 line[3]=self.replace_param(line[3]) # 再把引數進行轉換成字典 line[2]=self.str_to_dict(line[2]) line[3]=self.str_to_dict(line[3]) case_list.append(line) except Exception as e: log.error("讀取檔案出錯,檔名是:%s"%file_path) log.error("具體的錯誤資訊是%s"%traceback.format_exc()) return case_list def replace_param(self,s): """ 進行引數替換 :param s: :return: """ for func_name,func in func_map.items(): if func_name in s: result=func() s=s.replace(func_name,result) return s def str_to_dict(self,s): """ 字串轉換成字典,如果是空字串直接返回空字典 :param s: :return: """ d={} # 判斷引數是否為空 if s.strip(): # 字串透過逗號進行分割後透過等號進行分割後再轉換成字典 for i in s.split(","): k,v=i.split("=") d[k]=v return d
C.request.py
import traceback import requests from config import setting from urllib.parse import urljoin import nnlog log=nnlog.Logger(setting.LOG_PATH) class MyRequest: def __init__(self,url,method,data=None,headers=None,is_json='否'): # 拼接url,透過urljoin進行拼裝url self.url=urljoin(setting.HOST,url) self.data=data self.headers=headers self.is_json=is_json self.method=method.lower() self.req() def req(self): try: if self.is_json=='是': response= requests.request(self.method,self.url,params=self.data,json=self.data,headers=self.headers).json() else: response= requests.request(self.method,self.url,params=self.data,data=self.data,headers=self.headers).json() except Exception as e: log.error("請求%s的時候出錯了,請求引數是%s,錯誤資訊是%s"%(self.url,self.data,traceback.format_exc())) self.result={"msg":"請求介面出錯了","error_msg":traceback.format_exc()} self.text='{"msg":"請求介面出錯了","error_msg":%s}'%(traceback.format_exc()) else: self.result=response self.text=str(response)
D.parse_response.py
from lib.utils import get_value from config.setting import LOG_PATH import nnlog log=nnlog.Logger(LOG_PATH) class ParseResponse: fuhao=['!=','>=','<=','>','<','='] def __init__(self,check_str,response): self.check_str=check_str self.response=response self.reason="都透過了" self.status='透過' self.check_response() def check_response(self): """ 預期結果格式是:yuqijieguo="status=1,score>60,age!=15" :return: """ if self.check_str.strip(): for s in self.check_str.split(","): for f in self.fuhao: if f in s: # 分割取出預期結果中的key key, value = s.split(f) # 取到實際結果 shijijieguo = get_value(self.response, key) # 如果是實際結果型別是字串的話,實際結果和預期結果都轉換成字串進行比較 if type(shijijieguo) == str: shijijieguo = "'%s'" % (shijijieguo) value = "'%s'" % (value) # 轉換成字串進行eval執行 f = "==" if f == "=" else f code = "%s %s %s" % (shijijieguo, f, value) tag=eval(code) # 判斷tag不是true時表示有預期結果與實際結果對比是失敗的 if tag!=True: self.reason='key是%s,運算的程式碼是%s'%(key,code) log.debug(self.reason) self.status='失敗' return False break return True
5.bin檔案下定義start.py檔案是程式主入口;duoxiancheng.py是檔案程式多執行緒執行入口
A. start.py
#! /usr/bin python #-*- encoding=utf-8 -*- from lib.read_case import ParamDeal from lib.request import MyRequest from lib.parse_response import get_value,ParseResponse from lib.utils import send_email,write_excel import os from config.setting import CASE_PATH class CaseRun: all_count=0 pass_count=0 def get_case(self): # 獲取excel測試用例檔案 excel_list=[] for file in os.listdir(CASE_PATH): if file.startswith("test") and (file.endswith('.xls') or file.endswith(".xlsx")): abs_path = os.path.join(CASE_PATH, file) excel_list.append(abs_path) return excel_list def run_case(self,file_name): read_case_obj=ParamDeal() # 根據excel檔案獲取其中的測試用例 case_list=read_case_obj.read_excel(file_name) # response_list用於存放所有的結果 response_list=[] # 迴圈用例列表進行傳送請求 for case in case_list: # 統計用例的總個數,迴圈一次,用例加一條 self.all_count+=1 # 傳送介面請求 req_obj=MyRequest(*case[:5]) # 校驗結果 response_obj=ParseResponse(case[-1],req_obj.result) if response_obj.status=='透過': # 統計用例執行個數 self.pass_count+=1 response_list.append([str(req_obj.data),req_obj.text,response_obj.status,response_obj.reason]) return write_excel(file_name,response_list) def main(self): report_list=[] excel_list=self.get_case() for excel in excel_list: report_name=self.run_case(excel) report_list.append(report_name) send_email(self.all_count,self.pass_count,report_list) if __name__=="__main__": c=CaseRun() c.main()
B. duoxiancheng.py
import os,sys sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from lib.read_case import ParamDeal from lib.request import MyRequest from lib.parse_response import get_value,ParseResponse from lib.utils import send_email,write_excel from config.setting import CASE_PATH import threading class CaseRun: all_count=0 pass_count=0 report_list = [] def get_case(self): # 獲取excel測試用例檔案 excel_list=[] for file in os.listdir(CASE_PATH): if file.startswith("test") and (file.endswith('.xls') or file.endswith(".xlsx")): abs_path = os.path.join(CASE_PATH, file) excel_list.append(abs_path) return excel_list def run_case(self,file_name): read_case_obj=ParamDeal() # 根據excel檔案獲取其中的測試用例 case_list=read_case_obj.read_excel(file_name) # response_list用於存放所有的結果 response_list=[] # 迴圈用例列表進行傳送請求 for case in case_list: # 統計用例的總個數,迴圈一次,用例加一條 self.all_count+=1 # 傳送介面請求 req_obj=MyRequest(*case[:5]) # 校驗結果 response_obj=ParseResponse(case[-1],req_obj.result) if response_obj.status=='透過': # 統計用例執行個數 self.pass_count+=1 response_list.append([str(req_obj.data),req_obj.text,response_obj.status,response_obj.reason]) report_name= write_excel(file_name,response_list) self.report_list.append(report_name) def main(self): """ 啟動多執行緒執行 :return: """ excel_list=self.get_case() for excel in excel_list: t=threading.Thread(target=self.run_case,args=(excel,)) t.start() while threading.active_count()!=1: pass send_email(self.all_count,self.pass_count,self.report_list) if __name__ == '__main__': c=CaseRun() c.main()