本文的主题是自动化测试框架的实现,在实现之前,先了解一下关于unittest模块的相关知识:
Python中有一个自带的单元测试框架是unittest模块,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作。
在说unittest之前,先说几个概念:
TestCase 也就是测试用例
TestSuite 多个测试用例集合在一起,就是TestSuite
TestLoader是用来加载TestCase到TestSuite中的
TestRunner是来执行测试用例的,测试的结果会保存到TestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。
1.yaml测试用例文件
unittest做自动化测试时,读取的测试用例文件是 ".yml"或者“.yaml”格式的用例文件,首先需要安装pyyaml,直接 pip install pyyaml等待安装完成即可;
编写yaml格式的测试用例文件,我们以一个登录接口为例,来写测试用例,文件名是:login.yaml 如下是3条测试用例:
- url : /api/user/login #接口地址 method : post #请求方式 detail : 正常登录 #用例描述 data : #请求体 username : niuhanyang passwd : aA123456 check : #预期结果 - userId - sign - url : /api/user/login method : post detail : 密码错误 data : username : niuhanyang passwd : aA12333 check : - 密码错误 - url : /api/user/login method : post detail : 不传密码 data : username : niuhanyang check : - 必填参数未填
2.框架搭建
(1)首先需要将框架的路径建好,我这个框架在utp目录里面,utp目录下的具体目录层次如下:
bin目录存放执行的入口文件;
case_data存放的是yaml或者yml的用例文件;
case目录下存放自动生成的Python文件;
conf目录存放的是配置文件;
lib目录下存放的是主程序文件;
logs目录下存放的是日志文件;
report目录存放的是测试报告文件;
readme是说明;
(2)设置配置文件,在conf目录下创建setting.py文件,具体配置文件内容如下:
import os BASE_PATH = os.path.dirname( os.path.dirname(os.path.abspath(__file__)) )#基础路径,就是utp的路劲地址 MAIL_HOST='smtp.qq.com' MAIL_USER='605130685@qq.com' MAIL_PASSWRD = 'xxxxx'#邮箱的授权码 TO = [ 'lyxiehong@@qq.com', ] LEVEL = 'debug' #日志级别 LOG_PATH = os.path.join(BASE_PATH,'logs') #存放日志的路径 CASE_PATH = os.path.join(BASE_PATH,'cases') #存放用例的路径 YAML_PATH = os.path.join(BASE_PATH,'case_data') #存放yaml文件的路径 CASE_TEMPLATE = os.path.join(BASE_PATH,'conf','base.txt') #用例模板的路径 REPORT_PATH = os.path.join(BASE_PATH,'report') #存放测试报告的目录 BASE_URL = 'http://127.0.0.1' #接口的地址 LOG_NAME='utp.log' #日志的文件名
(3)写好请求接口的测试脚本,以登录接口为列,测试脚本写为base.txt格式的文件,具体脚本如下:
import unittest,requests import ddt from BeautifulReport import BeautifulReport as bf from urllib import parse from conf.setting import BASE_URL @ddt.ddt class %s(unittest.TestCase): base_url = BASE_URL @ddt.file_data(r'%s')#ddt帮你读文件,获取文件内容,循环调用函数 def test_request(self,**kwargs): detail = kwargs.get('detail','没写用例描述') self._testMethodDoc = detail #动态的用例描述 url = kwargs.get('url')#url url = parse.urljoin(self.base_url,url)#拼接好url method = kwargs.get('method','get')#请求方式 data = kwargs.get('data',{}) #请求参数 header = kwargs.get('header',{})#请求头 cookie = kwargs.get('cookie',{})#cookie check = kwargs.get('check') method = method.lower() #便于处理 try: if method=='get': res = requests.get(url,params=data,cookies=cookie,headers=header).text #因为接口有异常的情况下, 可能返回的不是json串,会报错 else: res = requests.post(url,data=data,cookies=cookie,headers=header).text except Exception as e: print('接口请求出错') res = e for c in check: self.assertIn(c,res,msg='预计结果不符,预期结果:'+c + '实际结果:' +res)
将此base.txt的文件放在cases目录下,后面会用到此文件作为基础文件进行copy成Python文件的测试用例。
在测试时,如果有多个接口需要进行测试,那么就需要在case_data目录下将每个接口的测试用例写好,每个接口写一个yaml用例文件,每个yaml文件中会有好几条用例,
例如登录接口中,1.登录成功,2密码错误,3.必填参数未填,这就有3条用例;在跑测试的时候,所有yaml文件中的用例总数加起来的数量就是执行的用例数量;
我们的思路是,在case_data目录下有几个yaml测试用例文件,就把base.txt文件自动生成几份Python测试文件,Python测试文件的名字以yaml的文件名作为文件名,然后把所有的Python测试文件都执行一遍,
然后得出测试报告,最后测试结果邮件。为了看出效果,我在case_data目录又写了一个注册接口的yaml测试用例文件,名字叫reg.yaml,这个文件中只写 了一条用例,如下:
- url : /api/user/user_reg method : post detail : 用户名字已经被注册的 data : username : xiehong passwd : aA123456 cpasswd : aA123456 check : - 用户已存在
(4)根据case_data目录下的yaml文件的数量,在lib目录下新建tools.py文件,写自动生成Python测试文件的代码,一般主体代码都写在lib目录下,如下:
import datetime,os,yagmail,unittest from conf import setting from BeautifulReport import BeautifulReport as bf def makeCase(): #这个函数自动生成Python文件 all_yaml = os.listdir(setting.YAML_PATH)#获取到YAML_PATH路径下的文件 base_case_str = open(setting.CASE_TEMPLATE,encoding='utf-8').read()#读取到文件里的东西 for yaml in all_yaml: if yaml.endswith('.yaml') or yaml.endswith('.yml'):#判断是否是yml文件 class_name = yaml.replace('.yml','').replace('.yaml','').capitalize()#获取yaml文件的名字作为类名,并首字母大写 file_name = os.path.join(setting.YAML_PATH,yaml)#拼接用例yaml文件的绝对路径 content = base_case_str %(class_name,file_name) #将读取到的文件中字符格式化赋值,就是即将生成Python文件的类名和文件名 py_file_name = os.path.join(setting.CASE_PATH,class_name)#拼好python文件的绝对路径 open('%s.py'%py_file_name,'w',encoding='utf-8').write(content)#写文件,content是写的内容
上面已经根据yaml文件的数量2,自动生成了2个Python测试文件,并且放在了cases目录下面,Login.py是测试登录接口的,Reg.py 是测试注册接口的。截图如下:
(6)接着在tools.py中写代码用来执行cases下的测试用例文件并生成测试报告,如下:
def run_all_case():#这个函数是运行所有的测试用例并生成测试报告 suite = unittest.TestSuite()#生成测试集合 all_py = unittest.defaultTestLoader.discover(setting.CASE_PATH,'*.py') #找到CASE_PATH目录下的所有Python文件 [ suite.addTests(py) for py in all_py] #列表生成式,添加文件里面的case到测试集合里面 run=bf(suite)#执行测试用例 today = datetime.datetime.today().strftime('%Y%m%d%H%M%S')#取当天的日期,精确到秒 title = '%s_接口测报告.html'%today#测试报告的文件名字 report_abs_path = os.path.join(setting.REPORT_PATH,title) #存放测试报告的路径 run.report(title,filename=title,log_path=setting.REPORT_PATH) return run.success_count,run.failure_count,report_abs_path#返回成功和失败数量,以及测试报告的路径
(7)用例执行完了测试报告也生成了,接下来就是要把测试结果发邮件,还是继续在tools.py文件中写发送邮件的代码,如下:
def sendmail(title,content,attrs=None):#这个函数发送测试结果邮件 m = yagmail.SMTP(host=setting.MAIL_HOST,user=setting.MAIL_USER ,password=setting.MAIL_PASSWRD ,smtp_ssl=True )#调用配置文件的配置数据 m.send(to=setting.TO,subject=title, contents=content, attachments=attrs)#setting.TO调用配置文件的配置数据
(8)主体代码基本上都写完了,接下来就要写执行文件了,也就是这些代码从哪调用连贯起来执行,需要提供一个执行的入口,这个入口文件就放在bin目录下,
我们把这个执行入口的文件起名叫run.py,具体代码:
import os,sys import datetime BASE_PATH = os.path.dirname( os.path.dirname(os.path.abspath(__file__)) ) sys.path.insert(0,BASE_PATH)#将utp目录加入环境变量 from lib import tools def mail(): tools.makeCase()#调用函数自动产生python文件 pass_count,fail_count,abs_path = tools.run_all_case() #调用函数运行用例,将返回结果赋值给pass_count,fail_count,abs_path msg=''' 各位好! 本次接口测试结果如下: 通过用例:%s条 失败用例:%s条 详细信息见附件【%s】。 '''%(pass_count,fail_count,os.path.basename(abs_path)) #获取abs_path的最后一级,就是报告的名称 today = datetime.datetime.today().strftime('%Y%m%d%H%M%S') title = '接口测试报告_%s'%today #邮件标题 tools.sendmail(title,msg,abs_path) #发送邮件 mail()
至此,一个自动化测试框架基本完成,如有错误,欢迎指出!