先别急着创建runAll.py文件(所有工作做完,最后我们运行runAll.py文件来执行接口自动化的测试工作并生成测试报告发送报告到相关人邮箱),但是我们在创建此文件前,还缺少点东东。按我的目录结构创建caselist.txt文件,内容如下:
user/test01case
#user/test02case
#user/test03case
#user/test04case
#user/test05case
#shop/test_shop_list
#shop/test_my_shop
#shop/test_new_shop
这个文件的作用是,我们通过这个文件来控制,执行哪些模块下的哪些unittest用例文件。如在实际的项目中:user模块下的test01case.py,店铺shop模块下的我的店铺my_shop,如果本轮无需执行哪些模块的用例的话,就在前面添加#。我们继续往下走,还缺少一个发送邮件的文件。在common下创建configEmail.py文件,内容如下:
import os
import win32com.client as win32
import datetime
import readConfig
import getpathInfo
read_conf = readConfig.ReadConfig()
subject = read_conf.get_email('subject')#从配置文件中读取,邮件主题
app = str(read_conf.get_email('app'))#从配置文件中读取,邮件类型
addressee = read_conf.get_email('addressee')#从配置文件中读取,邮件收件人
cc = read_conf.get_email('cc')#从配置文件中读取,邮件抄送人
mail_path = os.path.join(getpathInfo.get_Path(), 'result', 'report.html')#获取测试报告路径
class send_email():
def outlook(self):
olook = win32.Dispatch("%s.Application" % app)
mail = olook.CreateItem(win32.constants.olMailItem)
mail.To = addressee # 收件人
mail.CC = cc # 抄送
mail.Subject = str(datetime.datetime.now())[0:19]+'%s' %subject#邮件主题
mail.Attachments.Add(mail_path, 1, 1, "myFile")
content = """
执行测试中……
测试已完成!!
生成报告中……
报告已生成……
报告已邮件发送!!
"""
mail.Body = content
mail.Send()
if __name__ == '__main__':# 运营此文件来验证写的send_email是否正确
print(subject)
send_email().outlook()
print("send email ok!!!!!!!!!!")
运行configEmail.py验证邮件发送是否正确
邮件已发送成功,我们进入到邮箱中进行查看,一切OK~~不过这我要说明一下,我写的send_email是调用的outlook,如果您的电脑本地是使用的其他邮件服务器的话,这块的代码需要修改为您想使用的邮箱调用代码
继续往下走,这下我们该创建我们的runAll.py文件了
import os
import common.HTMLTestRunner as HTMLTestRunner
import getpathInfo
import unittest
import readConfig
from common.configEmail import send_email
from apscheduler.schedulers.blocking import BlockingScheduler
import pythoncom
# import common.Log
send_mail = send_email()
path = getpathInfo.get_Path()
report_path = os.path.join(path, 'result')
on_off = readConfig.ReadConfig().get_email('on_off')
# log = common.Log.logger
class AllTest:#定义一个类AllTest
def __init__(self):#初始化一些参数和数据
global resultPath
resultPath = os.path.join(report_path, "report.html")#result/report.html
self.caseListFile = os.path.join(path, "caselist.txt")#配置执行哪些测试文件的配置文件路径
self.caseFile = os.path.join(path, "testCase")#真正的测试断言文件路径
self.caseList = []
def set_case_list(self):
"""
读取caselist.txt文件中的用例名称,并添加到caselist元素组
:return:
"""
fb = open(self.caseListFile)
for value in fb.readlines():
data = str(value)
if data != '' and not data.startswith("#"):# 如果data非空且不以#开头
self.caseList.append(data.replace("\n", ""))#读取每行数据会将换行转换为\n,去掉每行数据中的\n
fb.close()
def set_case_suite(self):
"""
:return:
"""
self.set_case_list()#通过set_case_list()拿到caselist元素组
test_suite = unittest.TestSuite()
suite_module = []
for case in self.caseList:#从caselist元素组中循环取出case
case_name = case.split("/")[-1]#通过split函数来将aaa/bbb分割字符串,-1取后面,0取前面
print(case_name+".py")#打印出取出来的名称
#批量加载用例,第一个参数为用例存放路径,第一个参数为路径文件名
discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + '.py', top_level_dir=None)
suite_module.append(discover)#将discover存入suite_module元素组
print('suite_module:'+str(suite_module))
if len(suite_module) > 0:#判断suite_module元素组是否存在元素
for suite in suite_module:#如果存在,循环取出元素组内容,命名为suite
for test_name in suite:#从discover中取出test_name,使用addTest添加到测试集
test_suite.addTest(test_name)
else:
print('else:')
return None
return test_suite#返回测试集
def run(self):
"""
run test
:return:
"""
try:
suit = self.set_case_suite()#调用set_case_suite获取test_suite
print('try')
print(str(suit))
if suit is not None:#判断test_suite是否为空
print('if-suit')
fp = open(resultPath, 'wb')#打开result/20181108/report.html测试报告文件,如果不存在就创建
#调用HTMLTestRunner
runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='Test Report', description='Test Description')
runner.run(suit)
else:
print("Have no case to test.")
except Exception as ex:
print(str(ex))
#log.info(str(ex))
finally:
print("*********TEST END*********")
#log.info("*********TEST END*********")
fp.close()
#判断邮件发送的开关
if on_off == 'on':
send_mail.outlook()
else:
print("邮件发送开关配置关闭,请打开开关后可正常自动发送测试报告")
# pythoncom.CoInitialize()
# scheduler = BlockingScheduler()
# scheduler.add_job(AllTest().run, 'cron', day_of_week='1-5', hour=14, minute=59)
# scheduler.start()
if __name__ == '__main__':
AllTest().run()
执行runAll.py,进到邮箱中查看发送的测试结果报告,打开查看
然后继续,我们框架到这里就算基本搭建好了,但是缺少日志的输出,在一些关键的参数调用的地方我们来输出一些日志。从而更方便的来维护和查找问题。
按目录结构继续在common下创建Log.py,内容如下:
import os
import logging
from logging.handlers import TimedRotatingFileHandler
import getpathInfo
path = getpathInfo.get_Path()
log_path = os.path.join(path, 'result') # 存放log文件的路径
class Logger(object):
def __init__(self, logger_name='logs…'):
self.logger = logging.getLogger(logger_name)
logging.root.setLevel(logging.NOTSET)
self.log_file_name = 'logs' # 日志文件的名称
self.backup_count = 5 # 最多存放日志的数量
# 日志输出级别
self.console_output_level = 'WARNING'
self.file_output_level = 'DEBUG'
# 日志输出格式
self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
def get_logger(self):
"""在logger中添加日志句柄并返回,如果logger已有句柄,则直接返回"""
if not self.logger.handlers: # 避免重复日志
console_handler = logging.StreamHandler()
console_handler.setFormatter(self.formatter)
console_handler.setLevel(self.console_output_level)
self.logger.addHandler(console_handler)
# 每天重新创建一个日志文件,最多保留backup_count份
file_handler = TimedRotatingFileHandler(filename=os.path.join(log_path, self.log_file_name), when='D',
interval=1, backupCount=self.backup_count, delay=True,
encoding='utf-8')
file_handler.setFormatter(self.formatter)
file_handler.setLevel(self.file_output_level)
self.logger.addHandler(file_handler)
return self.logger
logger = Logger().get_logger()
然后我们在需要我们输出日志的地方添加日志:
我们修改runAll.py文件,在顶部增加import common.Log,然后增加标红框的代码
让我们再来运行一下runAll.py文件,发现在result下多了一个logs文件,我们打开看一下有没有我们打印的日志
OK,至此我们的接口自动化测试的框架就搭建完了,后续我们可以将此框架进行进一步优化改造,使用我们真实项目的接口,结合持续集成定时任务等,让这个项目每天定时的来跑啦~~~