atp接口自动化框架
程序说明:接口自动化框架,主要是通过读Excel中的接口用例,然后自动化跑用例
实现功能:读Excel,调用接口,结果校验,写报告,及发送报告
依赖软件:程序运行需要使用request,xlrd,nnlog,yagmail,xlutils,faker,jsonpath第三方模块,安装命令pip3 install 模块名称
程序运行:Python bin/start.py
程序结构,如下图:
代码如下:
1、setting配置文件
import os import faker #邮箱信息 email_info={ 'user': 'xx@qq.com', 'host': 'smtp.qq.com', 'password': 'XX' } #发件人和抄送人信息 to=['XX@qq.com'] cc=['XX@qq.com'] #每个目录的路径 base_path=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) log_path=os.path.join(base_path,'logs','atp.log') report_path=os.path.join(base_path,'report') cases_path=os.path.join(base_path,'cases') #case文件前缀 case_file_start='case' #测试数据生成 f=faker.Faker(locale='zh-CN') fun_map={ "<phone>":f.phone_number, "<id_card>":f.ssn, "<email>":f.email, "<name>":f.name, "<addr>":f.address, "<password>":f.ssn } #测试环境配置,可通过修改evn,进行线上,线下及准生产环境的自由切换 evn='test' host_map={ "test":"http://XX", "dev":"http://XX/", "pre":"http://XX/", } host=host_map[evn]
2、lib下的各功能模块
首先是读Excel,读用例模块
import traceback import xlrd import nnlog from config.setting import log_path,fun_map class Read_Case: def __init__(self): self.log = nnlog.Logger(log_path) #声明日志对象 #用例读取函数 def read_excel(self,xls_path): case_list = [] #定义用例存放list try: book = xlrd.open_workbook(xls_path) #读取用例文件 sheet1 = book.sheet_by_index(0) #声明使用文本簿第0个 for row in range (1,sheet1.nrows): #从第1行开始读取用例,第0行是表头,所以不用读取 row_case=sheet1.row_values(row)[1:7] #读取1-6列数据,放入list row_case[2]=self.parameter_change_dic(self.replace_parameter(row_case[2])) #对入参进行参数化替换及数据转换为字典 row_case[3]=self.parameter_change_dic(self.replace_parameter(row_case[3])) #对请求头进行参数化替换及数据转为字典 case_list.append(row_case) #逐行用例放入list中 return case_list #返回用例集 except Exception as e: #如果报错,报错信息写入LOG self.log.error("读取用例文件出错,文件名是:%s"%xls_path) self.log.error("具体错误信息是%s"%traceback.format_exc()) #参数化函数 def replace_parameter(self,str): for parameter in fun_map.keys(): #变量参数化名称 if parameter in str: #如果字符串中存在定义的参数化名进行参数化替换 str=str.replace(parameter,fun_map[parameter]()) #把随机生成的参数化值替换到STR中 return str #把参数化替换后的STR返回 #传参字符串转换字典 def parameter_change_dic(self,str): parameter_dic={} #字典 if str.strip(): #如果字符串非空 middle_value=str.split(',') #先对字符串逗号分隔放入LIST for value in middle_value: #对list每一项进行KEY和VALUE放入字典 kname,vname=value.split('=')#等号区分,分别读取KEY和VALUE值 parameter_dic[kname]=vname #逐项加入字典 return parameter_dic #返回转换后的字典
执行用例功能模块
import traceback import requests,nnlog from config.setting import host,log_path from urllib.parse import urljoin class Test_Request: def __init__(self,url,method,data,header,is_json='否'): #参数传参与EXCEL顺序保持一致 self.method=method.lower() self.url=urljoin(host,url) #host+接口名称拼接,urljoin可以拼接连接,无需考虑多斜杠,少斜杠的问题 self.data=data #传参 self.is_json=is_json #是否是JSON格式 self.header=header #请求头 self.log=nnlog.Logger(log_path) #日志对象声明 self.test_request() #类中调用方法,声明对象后无需单独执行方法 def test_request(self): try: if self.is_json=='是': #如果是json格式,请求参数用JSON case_result=requests.request(self.method,self.url,json=self.data,headers=self.header).json() else: #如果不是JSON格式,请求参数用data case_result=requests.request(self.method,self.url,data=self.data,headers=self.header).json() except Exception as e: #上面的请求出错后,就行写日志 self.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=case_result #JSON格式的返回结果 self.text=str(case_result) #返回结果转字符串类型
处理接口返回结果,实际结果与预期对比
import nnlog from config.setting import log_path import jsonpath class Test_Response: def __init__(self,check_str,response_str): self.log = nnlog.Logger(log_path) #声明日志对象 self.check_str=check_str #预期结果 self.response_str=response_str #实际返回结果 self.status='通过' #执行状态 self.reason="都通过啦" #不通过的原因 self.check_response() #类中调用方法,声明对象后无需单独执行方法 def check_response(self): symbol = ['!=', '>=', '<=', '=', '>', '<'] #符号库 middle_value=self.check_str.strip().split(',') #对预期字符串去空格,逗号分隔 for c_value in middle_value: #遍历预期list for s_mark in symbol: #遍历符合库 if s_mark in c_value: #假如符号在本次遍历的预期串中,比如encode=0 v_key,v_value=c_value.strip().split(s_mark) #预期串按符号分隔 case_result=self.get_value(self.response_str,v_key) #获取实际结果中包含该KEY的值 s_mark="==" if s_mark=='=' else s_mark #假如符号是=号,变==,为实际结果和预期结果对比 code="%s %s %s"%(case_result,s_mark,v_value) #实际结果,符号,预期结果组合 code_result=eval(code) #字符串执行命令 if not code_result: #假如实际与预期不符,打印日志 self.reason = 'key是%s,运算的代码是%s' % (v_key, code) self.log.error(self.reason) #输出错误原因 self.status = '失败' #执行状态为失败 return False break return True def get_value(self,c_result, k_value): '这个函数是用来从返回结果里面获取key的' result = jsonpath.jsonpath(c_result, '$..%s' % k_value) #获取key为k_value的值 if result: #假如result不为空 return result[0] #返回获取到的第一个 return '' #否则返回空
写报告
import time import xlrd import nnlog from xlutils import copy import os from config.setting import log_path, report_path log=nnlog.Logger(log_path) def write_excel(file_name,result_list): log.debug('现在开始写报告了:文件名是%s 内容:%s'%(file_name,result_list)) #写报告日志,注意如果返回结果太大,请不要打印result_list book=xlrd.open_workbook(file_name) #打开用例文件 new_book=copy.copy(book) #复制该文件 sheet=new_book.get_sheet(0) #获取第0个sheet for row,result in enumerate(result_list,1): #遍历返回结果列表,行从第1行开始写 for col,value in enumerate(result[1:],7): #遍历返回结果且从下标1开始,因为第一个结果放请求参数,列从第7列开始 sheet.write(row,col,value) #遍历写实际结果,原因,状态 sheet.write(row,3,result[0]) #最后写请求参数,也就是更新请求参数为实际发送请求时的参数值 file_name=os.path.split(file_name)[-1].replace('xlsx','xls') #获取文件名,且把xls替换为xlsx new_file_name=time.strftime('%Y%m%d%H%M%S')+"_"+file_name #新文件名称有当前时间+原文件名构成 abs_path=os.path.join(report_path,new_file_name) #新文件名+报告地址,构成最新的文件路径 new_book.save(abs_path) #文件保存到报告路径下 log.debug("报告生成完成,文件名是%s"%abs_path) #打印报告地址 return abs_path #返回报告地址
写邮件
import traceback import yagmail import time import nnlog from config.setting import email_info,cc,to,log_path log=nnlog.Logger(log_path) #日志对象声明 def send_mail(all_num,pass_num,file_name): log.debug('开始发送邮件') #发送邮件日志 subject = "%s---接口测试结果" % time.strftime('%Y-%m-%d %H:%M:%S') #邮件标题 content = ''' 各位好: "本次接口测试完成,结果如下:测试用例总共【%s】条,通过用例【%s】条,失败用例【%s】条" 详细信息请看附件 '''%(all_num,pass_num,all_num-pass_num) #邮件内容 try: mail=yagmail.SMTP(**email_info) #解包方式传入参数 mail.send(to=to,cc=cc,subject=subject,contents=content,attachments=file_name) #发送邮件 except Exception as e: log.error("发送邮件出错了,错误信息是:\n%s"%traceback.format_exc()) #捕获错误信息 else: log.info("发送邮件成功") #发送成功日志
启动自动化文件
from config.setting import cases_path,case_file_start,log_path from lib.parse_response import Test_Response from lib.request import Test_Request from lib.read_case import Read_Case import os from lib.mail import send_mail from lib.write_report import write_excel import nnlog log=nnlog.Logger(log_path) #声明日志对象 class CaseRun: all_num=0 #总用例数 pass_num=0 #通过用例数 #获取用例函数 def get_case(self): excel_list=[] #用例文件列表 for file in os.listdir(cases_path):#遍历用例文件夹下的文件列表 if file.startswith(case_file_start) and (file.endswith("xls") or file.endswith("xlsx")): #如果是规定的用例格式文件 abs_path=os.path.join(cases_path,file) #文件名和路径拼接 excel_list.append(abs_path) #用例文件路径放入用例文件list return excel_list #返回用例文件list #执行用例用例 def run_case(self,file_name): read_case_obj=Read_Case() #声明读用例对象 case_list=read_case_obj.read_excel(file_name) #对用例且放入用例list response_list=[] #声明返回结果list for case in case_list: #遍历用例list self.all_num+=1 #每次循环用例总数加1 req_obj=Test_Request(*case[:5]) #解包方式传入用例的URL,data,method,is_json,header response_obj=Test_Response(case[-1],req_obj.result) #预期与实际结果对比 if response_obj.status=='通过': #加入预期与实际结果对比,状态返回通过 self.pass_num+=1 #通过数加1 response_list.append([str(req_obj.data),req_obj.text,response_obj.reason,response_obj.status]) #通过还是失败,需要把实际请求参数,返回结果,原因,状态放入返回结果list return write_excel(file_name,response_list) #返回报告地址 #主函数 def main(self): report_list=[] #报告地址list excel_list=self.get_case() #用例文件列表 for excel in excel_list: #循环文件列表 report_name=self.run_case(excel) #执行用例 report_list.append(report_name) #每个用例的报告加入到报告list send_mail(self.all_num,self.pass_num,report_list) #发送邮件 log.debug('运行完成') #运行成功 if __name__=='__main__': run=CaseRun() #实例化主程序对象 run.main() #调用主方法,执行自动化测试用例
备注:当然还可以根据该框架更加丰富一下,比如测试数据通过数据读取,或者写方法自动生成,或外部文件读取,另外也可加入多种接口请求,比如XML等