自动化分为三类,数据驱动即根据数据去进行测试;代码驱动即测试都是通过代码完成的;关键字驱动即依靠一些软件,利用关键字完成测试。本次主要记录数据驱动,自动化框架主要有以下几个部分:
①获取用例
②调用接口
③检验结果
④发送测试报告
⑤异常处理
⑥日志
以前进行接口封装的时候,有过将代码分别放在不同的文件夹下的经历。这次也以这样的模式来完成,主要做到以下几点:首先读取excel文件里面的用例,其次根据用例完成接口测试,然后将测试返回信息和测试结果写入到excel用例文件中,最后发送邮件。同时各个部分关键结果处需要记录日志。
1、setting文件
setting文件一般放在config文件夹下。这个代码因为涉及到发送邮件,读取excel,写日志等。所以基础数据包括邮件的host,账号,密码等;还有文件地址和日志地址等。这些数据在后面的代码中都会使用到。
1 import os 2 3 4 # 发送邮件的一些固定参数,在sendmail函数中会使用到,可以在此处修改,此处的password是授权码 5 MAIL_HOST = 'smtp.163.com' 6 MAIL_USER = '********@163.com' 7 MAIL_PASSWORD = *****' # 授权码 8 MAIL_TO = '352780148@qq.com' 9 10 BASE_PATH = os.path.dirname( 11 os.path.dirname(os.path.abspath(__file__)) 12 ) # 找到APT这个文件夹的地址 13 LOG_PATH = os.path.join(BASE_PATH, 'logs') # 拼接出存放日志的路径 14 CASE_PATH = os.path.join(BASE_PATH, 'cases') # 拼接出存放用例的路径 15 16 LEVEL = 'debug' # 日志级别 17 LOG_NAME = 'apt.log' # 日志文件名
2、日志模块
这个模块主要内容就是日志记录的功能。构造了一个可实例化的类,实例化后在其他模块可以直接调用生成相应等级的日志。
1 import logging 2 import os 3 from logging import handlers 4 from config import setting 5 6 7 class MyLogger(object): 8 def __init__(self,file_name,level='info',backCount=5,when='D'): 9 logger = logging.getLogger() 10 logger.setLevel(self.get_level(level)) # 设置日志的级别 11 # fl = logging.FileHandler(filename='a.log', mode='a', encoding='utf-8') 12 cl = logging.StreamHandler() # 负责往控制台输出的 13 bl = handlers.TimedRotatingFileHandler(filename=file_name, when=when, interval=1, 14 backupCount=backCount, encoding='utf-8') 15 fmt = logging.Formatter('%(asctime)s - %(pathname)s[line:%(lineno)d]-%(levelname)s:%(message)s') 16 # 指定日志的格式 17 cl.setFormatter(fmt) # 设置控制台输出的日志格式 18 bl.setFormatter(fmt) # 设置文件里面写入的日志格式 19 logger.addHandler(cl) # 把已经设置好的人放到办公室中 20 logger.addHandler(bl) # 同上 21 self.logger = logger 22 23 def get_level(self, sss): 24 level = { 25 'debug': logging.DEBUG, 26 'info': logging.INFO, 27 'warn': logging.WARN, 28 'error': logging.ERROR 29 } 30 sss = sss.lower() 31 return level.get(sss) 32 33 path = os.path.join(setting.LOG_PATH, setting.LOG_NAME) # 拼好日志的绝对路径 34 apt_log = MyLogger(path, setting.LEVEL).logger 35 # 直接在这边实例化,并.logger,使用时就可以直接apt_log.warning了
3、发送邮件模块
用于记录发送邮件的功能,这里就用到了setting里面记录的那些基础数据,还用到了日志记录功能。
1 import yagmail 2 from config import setting 3 from lib.log import apt_log 4 5 6 def sendemile(title, content, attrs=None): # 定义发送邮件函数 7 m = yagmail.SMTP(host=setting.MAIL_HOST, # host调用setting文件中设置的固定参数 8 user=setting.MAIL_USER, # user调用setting文件中设置的固定参数 9 password=setting.MAIL_PASSWORD # password调用setting文件中设置的固定参数 10 ) 11 m.send(to=setting.MAIL_TO, 12 subject=title, 13 contents=content, 14 attachments=attrs 15 ) # 邮件发送主体内容,收件人使用固定参数,其他内容在调用函数时传入 16 apt_log.info('邮件发送成功。') # 写入发送邮件成功的日志
4、读取用例进行测试并记录测试结果模块
这是一个主体模块,完成了这个文件的主要功能。详细代码如下。
1 import xlrd 2 from lib.log import apt_log 3 import requests 4 from xlutils import copy 5 6 7 class OpCase(object): 8 def get_case(self, file_path): 9 cases = [] # 存放所有的测试用例 10 if file_path.endswith('.xls') or file_path.endswith('.xlsx'): # 判断路径是否存在 11 try: 12 book = xlrd.open_workbook(file_path) 13 sheet = book.sheet_by_index(0) # 打开用例文件 14 for i in range(1, sheet.nrows): # 从第二行开始遍历excel文件内容(第一行是标题) 15 row_data = sheet.row_values(i) # 获取每行内容 16 cases.append(row_data[4:8]) # 在cases这个list中存每个用例的url,method,req_data,check 17 apt_log.info('共读取%s条用例' % (len(cases))) # 检查cases中的元素个数,日志记录读取了几条用例 18 self.file_path = file_path # 既然读取成功说明这个文件路径是正确的,那么这边定义下面写exccel就可以直接使用了 19 except Exception as e: 20 apt_log.error('【%s】用例打开失败,错误信息:%s' % (file_path, e)) 21 else: 22 apt_log.error('用例文件不合法,%s' % file_path) 23 return cases 24 25 def dataToDict(self, data): # 将用例中的req_data转为字典格式,可以直接用在request模块 26 res = {} 27 data = data.split(',') # 这个地方要求用例中请求参数使用英文逗号分割的 28 # 这边分割入参后,入参变为['a=1', 'b=2']这样的格式 29 for d in data: 30 k, v = d.split('=') 31 # 再次分割后变为['a','1']这样的模式分别用k代表a,v代表1 32 res[k] = v # 存入字典中变成'a':1,这样的模式可直接在request模块接口测试中作为入参使用 33 return res 34 35 def my_request(self, url, method, data): 36 method = method.upper() # 首先将请求方式变为大写模式,方便后面验证 37 data = self.dataToDict(data) # 把a=1,b=2格式的请求参数变成字典格式 38 try: 39 if method == 'POST': 40 res = requests.post(url, data).text # 如果请求方式是post按照post模式进行接口测试 41 elif method == 'GET': 42 res = requests.get(url, data).text # 按照get方式传入url和data进行接口测试 43 else: 44 apt_log.warning('暂不支持该请求') # 其他的请求方式不支持,打印日志并返回提示 45 res = '暂不支持该请求' 46 except Exception as e: 47 msg = '【%s】接口调用失败,%s' % (url, e) # 请求未成功提示接口调用失败,写入日志并返回提示 48 apt_log.error(msg) 49 res = msg 50 return res 51 52 def check_res(self, res, check): 53 res.replace('": "', '=').replace('": ', '=') # 返回的报文是json格式的,利用字符替换让数据变成"a=1,b=2"的格式 54 for c in check.split(','): # 判断验证数据是否在返回的报文中 55 if c not in res: 56 apt_log.info('结果校验失败,预期结果:【%s】,实际结果【%s】' % (c, res)) # 不匹配则测试校验失败,打印日志 57 return '失败' # 返回失败的提示 58 return '通过' # 如果存在说明测试通过 59 60 def write_excel(self, cases_res): # 入参是每个用例的执行结果 61 book = xlrd.open_workbook(self.file_path) 62 new_book = copy.copy(book) 63 sheet = new_book.get_sheet(0) # 复制测试用例excel文件 64 row = 1 65 for case_res in cases_res: # 遍历写入数据 66 sheet.write(row, 8, case_res[0]) # 第八列写返回结果 67 sheet.write(row, 9, case_res[1]) # 第九列写测试是否通过 68 row += 1 69 new_book.save(self.file_path.replace('xlsx', 'xls')) # 保存结果 70
5、start文件
启动文件,用于集成所有的模块完成目标。
1 import os 2 import sys 3 from lib.common import OpCase 4 from lib.send_emile import sendemile 5 from config import setting 6 7 BASE_PATH = os.path.dirname( 8 os.path.dirname(os.path.abspath(__file__)) 9 ) # 找到APT这个文件夹的地址 10 sys.path.insert(0, BASE_PATH) # 把ATP目录加到环境变量中,用于在其他不能帮忙加环境变量的时候 11 12 13 class CaseRun(object): 14 def find_cases(self): 15 op = OpCase() # 实例化了操作用例这个类 16 for f in os.listdir(setting.CASE_PATH): # 获取到cases文件夹下的文件 17 abs_path = os.path.join(setting.CASE_PATH, f) # 拼接测试用例文件的绝对路径 18 case_list = op.get_case(abs_path) # 利用get_case函数获取到每个用例中的case 19 res_list = [] # 创建一个空的list来存放测试返回的报文和测试结果 20 pass_count = 0 # 用来计算成功用例个数 21 fail_count = 0 # 用来计算失败用例格式 22 for case in case_list: 23 url, method, req_data, check = case # get_case函数中提取每行用例的四个元素,分别定义一下 24 res = op.my_request(url, method, req_data) # 调用my_request函数进行测试接口返回结果 25 status = op.check_res(res, check) # 检查测试返回报文是否和预计测试结果相同,如果相同测试通过,否则测试失败 26 res_list.append([res, status]) # 为了一次性写入测试结果,先将结果放入list中 27 if status == '通过': 28 pass_count += 1 29 else: 30 fail_count += 1 31 op.write_excel(res_list) # 写入excel 32 msg = ''' # 定义邮件正文 33 XX你好: 34 本次共运行%s条用例,通过%s条,失败%s条。 35 ''' % (len(res_list), pass_count, fail_count) 36 sendemile('测试用例运行结果', content=msg, attrs=abs_path) # 利用sendmail函数发送邮件,入参分别是主题,内容和附件 37 38 39 CaseRun().find_cases() # 运行find_cases函数