• 2
  • 3
分享

  项目背景

  公司内部的软件采用B/S架构,目的是进行实验室的数据存储、分析、管理。大部分是数据的增删改查,但是由于还在开发阶段,所以UI的变化非常快,难以针对UI进行自动化测试,那样会消耗大量的精力与时间维护自动化脚本,对于小团队来说就得不偿失了。针对此种情况,选用接口测试较为有效。

  工具选择

  针对接口测试的自动化工具也很多,例如Soup UI、Postman、robotframework,甚至jmeter这样的性能测试工具都可以进行接口测试。

  robot framework测试框架有很多的第三方库可以使用,采用的是填表的方式进行,较容易上手,但是无法深入底层的了解客户端与服务器的交互过程。jmeter这样的专注性能测试的工具,进行接口测试,有点大材小用的感觉而且无法生成测试报告。但是这些工具灵活性不够,也不完全适用于被测系统。

  综上考虑,决定自己开发一个简单的框架,优点是足够灵活,可以随时根据需求进行变更,后台使用的是python+flask进行开发,此次选用python 2.7.11进行框架的开发,python开发的速度很快,且容易上手,丰富的第三方库,大大加快了开发速度和难度。

  框架思路

  由于是框架,所以要考虑到框架的可重用性和可维护性。其次,需要考虑到测试人员编写测试用例的方便性,采用数据驱动的设计方式,将数据分层出来,与业务逻辑剥离。这样测试人员就可以通过数据文件专注的写测试用例,不用关注代码编写,提高了效率。此次框架采用基本的excel进行数据管理。通过对excel 的读取获得数据。

  之后将测试的结果生成HTML格式的测试报告发送给相关开发人员。

  第三方库介绍

  Requests

  python中有许多针对http的库,例如自带的urllib2,但是自带的urllib2编写起来实在是太费精力,所以采用号称"HTTP for Humans"的requests库。

  xlrd

  xlrd使得python可以方便的对excel文件进行读写操作,此次通过xlrd读取excel文件中的测试数据。

  以上第三方库都可以通过pip直接安装或者通过pypi下载源码包安装。

  模块介绍

  get_conf:读取配置文件,获得邮件发送的配置信息,如smtpserver、receiver、sender等。

  md5Encode:部分数据采用md5加密后传输,所以需要把从excel读取的数据进行md5加密。

  sendMail:当测试完成后,将测试报告自动的发送给相关开发人员。

  runTest:此部分读取excel中的数据,调用下方的interfaceTest方法,保存interfaceTest返回的信息。

  interfaceTest:将runTest读取的excel数据作为入参,执行接口测试,并将后台返回的信息返回给runTest。

  Excel的文件格式如下图,API Purpose记录接口的名称,API Host记录主机地址,Request Methon记录请求方式,可以选择GET或者POST。

  Request Data就是构造的测试数据,这里需要注意其格式的书写。

  Check Point是检查点的设置,当获得数据后,需要跟检查点的数据进行比对,如果符合,说明测试成功,反之失败。

1.jpg

  配置文件的格式如下图:

2.jpg

  在这里可以设置邮件发送人、接收者、smtp服务器地址以及用户名密码。需要根据实际情况进行改变。

  代码实现

#通过自带的ConfigParser模块,读取邮件发送的配置文件,作为字典返回
import ConfigParser
def get_conf():
conf_file = ConfigParser.ConfigParser()
conf_file.read(os.path.join(os.getcwd(),'conf.ini'))
conf = {}
conf['sender'] = conf_file.get("email","sender")
conf['receiver'] = conf_file.get("email","receiver")
conf['smtpserver'] = conf_file.get("email","smtpserver")
conf['username'] = conf_file.get("email","username")
conf['password'] = conf_file.get("email","password")
return conf
#此处使用python自带的logging模块,用来作为测试日志,记录测试中系统产生的信息。
import logging,os
log_file = os.path.join(os.getcwd(),'log/sas.log')
log_format = '[%(asctime)s] [%(levelname)s] %(message)s'
logging.basicConfig(format=log_format, filename=log_file, filemode='w', level=logging.DEBUG)
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
formatter = logging.Formatter(log_format)
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
#读取testcase excel文件,获取测试数据,调用interfaceTest方法,将结果保存至errorCase列表中。
import xlrd,hashlib,json
def runTest(testCaseFile):
testCaseFile = os.path.join(os.getcwd(),testCaseFile)
if not os.path.exists(testCaseFile):
logging.error('测试用例文件不存在!')
sys.exit()
testCase = xlrd.open_workbook(testCaseFile)
table = testCase.sheet_by_index(0)
errorCase = []                #用于保存接口返回的内容和HTTP状态码
s = None
for i in range(1,table.nrows):
if table.cell(i, 9).vale.replace('\n','').replace('\r','') != 'Yes':
continue
num = str(int(table.cell(i, 0).value)).replace('\n','').replace('\r','')
api_purpose = table.cell(i, 1).value.replace('\n','').replace('\r','')
api_host = table.cell(i, 2).value.replace('\n','').replace('\r','')
request_method = table.cell(i, 4).value.replace('\n','').replace('\r','')
request_data_type = table.cell(i, 5).value.replace('\n','').replace('\r','')
request_data = table.cell(i, 6).value.replace('\n','').replace('\r','')
encryption = table.cell(i, 7).value.replace('\n','').replace('\r','')
check_point = table.cell(i, 8).value
if encryption == 'MD5':              #如果数据采用md5加密,便先将数据加密
request_data = json.loads(request_data)
request_data['pwd'] = md5Encode(request_data['pwd'])
status, resp, s = interfaceTest(num, api_purpose, api_host, request_url, request_data, check_point, request_methon, request_data_type, s)
if status != 200 or check_point not in resp:            #如果状态码不为200或者返回值中没有检查点的内容,那么证明接口产生错误,保存错误信息。
errorCase.append((num + ' ' + api_purpose, str(status), 'http://'+api_host+request_url, resp))
return errorCase
#接受runTest的传参,利用requests构造HTTP请求
import requests
def interfaceTest(num, api_purpose, api_host, request_method,
request_data_type, request_data, check_point, s=None)
headers = {'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With' : 'XMLHttpRequest',
'Connection' : 'keep-alive',
'Referer' : 'http://' + api_host,
'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36'
}
if s == None:
s = requests.session()
if request_method == 'POST':
if request_url != '/login' :
r = s.post(url='http://'+api_host+request_url, data = json.loads(request_data), headers = headers)         #由于此处数据没有经过加密,所以需要把Json格式字符串解码转换成**Python对象**
elif request_url == '/login' :
s = requests.session()
r = s.post(url='http://'+api_host+request_url, data = request_data, headers = headers)          #由于登录密码不能明文传输,采用MD5加密,在之前的代码中已经进行过json.loads()转换,所以此处不需要解码
else:
logging.error(num + ' ' + api_purpose + '  HTTP请求方法错误,请确认[Request Method]字段是否正确!!!')
s = None
return 400, resp, s
status = r.status_code
resp = r.text
print resp
if status == 200 :
if re.search(check_point, str(r.text)):
logging.info(num + ' ' + api_purpose + ' 成功,' + str(status) + ', ' + str(r.text))
return status, resp, s
else:
logging.error(num + ' ' + api_purpose + ' 失败!!!,[' + str(status) + '], ' + str(r.text))
return 200, resp , None
else:
logging.error(num + ' ' + api_purpose + '  失败!!!,[' + str(status) + '],' + str(r.text))
return status, resp.decode('utf-8'), None
import hashlib
def md5Encode(data):
hashobj = hashlib.md5()
hashobj.update(data.encode('utf-8'))
return hashobj.hexdigest()
def sendMail(text):
mail_info = get_conf()
sender = mail_info['sender']
receiver = mail_info['receiver']
subject = '[AutomationTest]接口自动化测试报告通知'
smtpserver = mail_info['smtpserver']
username = mail_info['username']
password = mail_info['password']
msg = MIMEText(text,'html','utf-8')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ''.join(receiver)
smtp = smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(username, password)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()
def main():
errorTest = runTest('TestCase/TestCase.xlsx')
if len(errorTest) > 0:
html = '<html><body>接口自动化定期扫描,共有 ' + str(len(errorTest)) + ' 个异常接口,列表如下:' + '</p><table><tr><th style="width:100px;text-align:left">接口</th><th style="width:50px;text-align:left">状态</th><th style="width:200px;text-align:left">接口地址</th><th   style="text-align:left">接口返回值</th></tr>'
for test in errorTest:
html = html + '<tr><td style="text-align:left">' + test[0] + '</td><td style="text-align:left">' + test[1] + '</td><td style="text-align:left">' + test[2] + '</td><td style="text-align:left">' + test[3] + '</td></tr>'
sendMail(html)
if __name__ == '__main__':
main()

  由于所有的操作必须在系统登录之后进行,一开始没有注意到cookie这一点,每读取一个测试用例,都会新建一个session,导致无法维护上一次请求的cookie。然后将cookie添加入请求头中,但是第二个用例仍然无法执行成功。后来用fiddler抓包分析了一下,发现cookie的值竟然是"每一次操作后都会变化的!!!"

  所以只能通过session自动维护cookie。

  在interfaceTest函数中,返回三个值,分别是HTTP CODE,HTTP返回值与session。再将上一次请求的session作为入参传入interfaceTest函数中,在函数内部判断session是否存在,如果不为None,那么直接利用传入的session执行下一个用例,如果为None,那么新建一个session。

  不足之处

  1. 框架十分简陋,只是简单想法的实现,对于编码的细节没有完善。

  2. HTML的测试报告书写起来比较麻烦,可以考虑引入第三方库进行HTML测试报告的书写,将生成的HTML文件作为附件发送。

  3. 只是针对公司内部的软件,换用其他平台就不适用,需要修改源码。


  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 质量保障的核心目标质量保障的核心目标在实际的项目或团队中很少有明确的约定或口径,就实际经验而言,可以归于一条:线上故障的减少。这个经验得来的目标实际是一个非常宽泛的目标了,通过团队成员的多方努力,这个目标仍然是“若即若离”。站在不同人员的角度,对测试目标会有共同的期望:故障的减少 & 人效的提升 & 迭代周期的缩短。但对测试结果的期望,线上故障的减少可以说是最核心的一个目标。产品故障的广泛定义从广义上来说,故障同时包括了:硬性质量引发的问题、软性质量引发的问题、需求定义引发的问题。硬性质量引发的问题指上线/配置修改等直接引发的线上不可用问题(用户直接不可用)软性质量的引发的问题...
            0 0 1110
            分享
          • 随着智能手机的普及,移动app测试越来越重要。现在很多互联网都把注意精力放在了移动端,移动app尽量提供完美的用户体验。但是诸如崩溃,冻结问题,加载时间慢,不直观的导航以及侵犯隐私之类的严重错误可能会触发用户立即卸载应用程序。现在,移动应用程序已成为我们日常微时刻不可或缺的一部分,人们平均每天花费3-4个小时。移动应用在职业和个人生活中对每个人都起着关键作用。因此,手机移动端测试在构建移动应用程序以提供流畅的用户体验和功能方面扮演着重要角色。移动应用测试金字塔软件测试的人都知道Mike Cohn的测试自动化金字塔。典型的金字塔由三层组成。顶部是自动化集成测试层的中间,是一个自动化的端到端测试层...
            1 0 1876
            分享
          •   一、过硬的基础能力  其实所有的测试大佬都是从底层基础开始的,随着时间,经验的积累慢慢变成大佬。要想稳扎稳打在测试行业深耕,成为测试大牛,首当其冲的肯定就是拥有过硬的基础,所有的基础都是根基,后期所有的发展和提升都是基于测试基础铺垫的。  所以核心的测试理论、测试用例设计方法、测试的方向、测试的分类,从简单的功能测试到高效的自动化测试、再从接口的工具使用,到性能测试,不管是做web端的项目还是app端的移动测试,都是基于基础,基于理论,基于核心的使用方法,只有拥有过硬的基础能力才能有序的持续发展。所以所有涉及到测试相关的基础都必须有广度的掌握,然后再进利用经验的积累,进行深度挖掘,非常熟练...
            0 0 368
            分享
          •   测试用例八大要素  1.测试用例编号  由字母、字符、数字组合而成的字符串,有唯一性,易识别性。  eg:  1)系统测试:产品编号-ST-系统测试项名-系统测试子项名-编号  2)集成测试:产品编号-IT-集成测试项名-集成测试子项名-编号  3)单元测试:产品编号-UT-单元测试项名-单元测试子项名-编号  这样看到编号就可以知道是做的什么测试,测试的对象是什么,也方便维护。  2.测试项目  当前测试用例所在测试用例所属大类、被测需求、被测模块、被测单元等。  3.测试用例标题  对测试用例的简单描述。用概括的语言描述该测试用例的测试点。每个测试用例的标题不能够重复,因为每个测试用例...
            0 0 1048
            分享
          • 一、DMLDML是Data Manipulation Language英文缩写,数据操作语言包括:INSERT ( 添加语句 )UPDATE ( 更新语句 )DELETE ( 删除语句 )1、INSERT语法:INSERT [INTO] 表名 [(列名)] VALUES (值列表);实例:INSERT INTO goods (name) VALUES ('饼干');1.1 插入单行数据注意:字段名是可选的,如省略依次插入所有字段。INSERT INTO goods VALUES (8,'肥皂',DEFAULT,'2...
            0 0 1637
            分享
      • 51testing软件测试圈微信