项目背景

公司内部的软件采用B/S架构,管理实验室数据,实现数据的存储和分析统计。大部分是数据的增删改查,由于还在开发阶段,所以UI界面的变化非常快,之前尝试过用python+selenium进行UI自动化测试,后来发现今天刚写好的脚本第二天前端就改了页面,又得重新去定位元素什么的,消耗大量的精力与时间维护自动化脚本。针对此种情况,对接口测试较为有效。

工具

由于开发那里不能提供后台代码给我,只能通过抓包分析。使用fiddler抓包,发现前端与后台都是采用POST方法交互,前端POST数据,后台返回数据。这样也比较简单粗暴,直接针对每个接口POST测试数据,然后观察返回值就好了。

使用excel编写测试用例和数据,requests发送HTTP请求。

功能模块

通过xlrd库读取excel中的测试用例和数据

requests负责发送数据并接收后台返回的数据

针对测试结果,需要保存测试日志并生成测试报告,采用python自带的logging记录日志,测试报告采用html格式。

同时测试完成后,需要将测试报告发送给相关的开发人员,需要有自动发送邮件的功能

目录结构

代码实现

  1. #通过自带的ConfigParser模块,读取邮件发送的配置文件,作为字典返回
  2. import ConfigParser
  3. def get_conf():
  4. conf_file = ConfigParser.ConfigParser()
  5. conf_file.read(os.path.join(os.getcwd(),'conf.ini'))
  6. conf = {}
  7. conf['sender'] = conf_file.get("email","sender")
  8. conf['receiver'] = conf_file.get("email","receiver")
  9. conf['smtpserver'] = conf_file.get("email","smtpserver")
  10. conf['username'] = conf_file.get("email","username")
  11. conf['password'] = conf_file.get("email","password")
  12. return conf

配置文件格式

这个logging不熟悉的可以google一下,还是挺有用的,需要自己配置一下。需要手动新建一个空白的.log文件。

  1. #此处使用python自带的logging模块,用来作为测试日志,记录测试中系统产生的信息。
  2. import logging,os
  3. log_file = os.path.join(os.getcwd(),'log/sas.log')
  4. log_format = '[%(asctime)s] [%(levelname)s] %(message)s' #配置log格式
  5. logging.basicConfig(format=log_format, filename=log_file, filemode='w', level=logging.DEBUG)
  6. console = logging.StreamHandler()
  7. console.setLevel(logging.DEBUG)
  8. formatter = logging.Formatter(log_format)
  9. console.setFormatter(formatter)
  10. logging.getLogger('').addHandler(console)

excel文件如下图

python读取excel跟读取一个二维数组差不多,下标也是从0开始

  1. #读取testcase excel文件,获取测试数据,调用interfaceTest方法,将结果保存至errorCase列表中。
  2. import xlrd,hashlib,json
  3. def runTest(testCaseFile):
  4. testCaseFile = os.path.join(os.getcwd(),testCaseFile)
  5. if not os.path.exists(testCaseFile):
  6. logging.error('测试用例文件不存在!')
  7. sys.exit()
  8. testCase = xlrd.open_workbook(testCaseFile)
  9. table = testCase.sheet_by_index(0)
  10. errorCase = [] #用于保存接口返回的内容和HTTP状态码
  11. s = None
  12. for i in range(1,table.nrows):
  13. if table.cell(i, 9).vale.replace('\n','').replace('\r','') != 'Yes':
  14. continue
  15. num = str(int(table.cell(i, 0).value)).replace('\n','').replace('\r','')
  16. api_purpose = table.cell(i, 1).value.replace('\n','').replace('\r','')
  17. api_host = table.cell(i, 2).value.replace('\n','').replace('\r','')
  18. request_method = table.cell(i, 4).value.replace('\n','').replace('\r','')
  19. request_data_type = table.cell(i, 5).value.replace('\n','').replace('\r','')
  20. request_data = table.cell(i, 6).value.replace('\n','').replace('\r','')
  21. encryption = table.cell(i, 7).value.replace('\n','').replace('\r','')
  22. check_point = table.cell(i, 8).value
  23. if encryption == 'MD5': #如果数据采用md5加密,便先将数据加密
  24. request_data = json.loads(request_data)
  25. request_data['pwd'] = md5Encode(request_data['pwd'])
  26. status, resp, s = interfaceTest(num, api_purpose, api_host, request_url, request_data, check_point, request_methon, request_data_type, s)
  27. if status != 200 or check_point not in resp: #如果状态码不为200或者返回值中没有检查点的内容,那么证明接口产生错误,保存错误信息。
  28. errorCase.append((num + ' ' + api_purpose, str(status), 'http://'+api_host+request_url, resp))
  29. return errorCase

下面的就是接口部分

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

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

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

  1. #接受runTest的传参,利用requests构造HTTP请求
  2. import requests
  3. def interfaceTest(num, api_purpose, api_host, request_method,
  4. request_data_type, request_data, check_point, s=None)
  5. headers = {'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
  6. 'X-Requested-With' : 'XMLHttpRequest',
  7. 'Connection' : 'keep-alive',
  8. 'Referer' : 'http://' + api_host,
  9. 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36'
  10. }
  11. if s == None:
  12. s = requests.session()
  13. if request_method == 'POST':
  14. if request_url != '/login' :
  15. r = s.post(url='http://'+api_host+request_url, data = json.loads(request_data), headers = headers) #由于此处数据没有经过加密,所以需要把Json格式字符串解码转换成**Python对象**
  16. elif request_url == '/login' :
  17. s = requests.session()
  18. r = s.post(url='http://'+api_host+request_url, data = request_data, headers = headers) #由于登录密码不能明文传输,采用MD5加密,在之前的代码中已经进行过json.loads()转换,所以此处不需要解码
  19. else:
  20. logging.error(num + ' ' + api_purpose + ' HTTP请求方法错误,请确认[Request Method]字段是否正确!!!')
  21. s = None
  22. return 400, resp, s
  23. status = r.status_code
  24. resp = r.text
  25. print resp
  26. if status == 200 :
  27. if re.search(check_point, str(r.text)):
  28. logging.info(num + ' ' + api_purpose + ' 成功,' + str(status) + ', ' + str(r.text))
  29. return status, resp, s
  30. else:
  31. logging.error(num + ' ' + api_purpose + ' 失败!!!,[' + str(status) + '], ' + str(r.text))
  32. return 200, resp , None
  33. else:
  34. logging.error(num + ' ' + api_purpose + ' 失败!!!,[' + str(status) + '],' + str(r.text))
  35. return status, resp.decode('utf-8'), None
  1. import hashlib
  2. def md5Encode(data):
  3. hashobj = hashlib.md5()
  4. hashobj.update(data.encode('utf-8'))
  5. return hashobj.hexdigest()
  6. def sendMail(text):
  7. mail_info = get_conf()
  8. sender = mail_info['sender']
  9. receiver = mail_info['receiver']
  10. subject = '[AutomationTest]接口自动化测试报告通知'
  11. smtpserver = mail_info['smtpserver']
  12. username = mail_info['username']
  13. password = mail_info['password']
  14. msg = MIMEText(text,'html','utf-8')
  15. msg['Subject'] = subject
  16. msg['From'] = sender
  17. msg['To'] = ''.join(receiver)
  18. smtp = smtplib.SMTP()
  19. smtp.connect(smtpserver)
  20. smtp.login(username, password)
  21. smtp.sendmail(sender, receiver, msg.as_string())
  22. smtp.quit()
  23. def main():
  24. errorTest = runTest('TestCase/TestCase.xlsx')
  25. if len(errorTest) > 0:
  26. 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>'
  27. for test in errorTest:
  28. 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>'
  29. sendMail(html)
  30. if __name__ == '__main__':
  31. main()

以上就是一个简单的接口测试实现,参考了论坛里的大神们很多东西,还有很多不足之处。例如html格式的邮件很简陋,可以考虑生成一个HTML的测试报告,作为附件发送。

接口方法只实现了一个POST。

编码也很随意,基本想到哪写到哪,不规范的地方请指正。

传送门:https://testerhome.com/topics/4948

基于 python 的接口测试框架的更多相关文章

  1. 【转】基于Python的接口测试框架实例

    下面小编就为大家带来一篇基于Python的接口测试框架实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧   背景 最近公司在做消息推送,那么自然就会产生很多接口,测试 ...

  2. 基于python的接口测试框架设计(三)接口测试的框架

    基于python的接口测试框架设计(三)接口测试的框架 其实我这里用到的是unittest单元测试框架,,这个框架好就好在比较清楚,,setup terdown都可以处理一些初始化及完成后的工作 主要 ...

  3. 基于python的接口测试框架设计(二)配置一些参数及文件

    基于python的接口测试框架设计(二)配置一些参数及文件 我这里需要基于我的项目配置的主要是登陆参数.以及baseURL ,把这些放在单独的文件里  毕竟导入的时候方便了一些 首先是url 图略 建 ...

  4. 基于python的接口测试框架设计(一)连接数据库

    基于python的接口测试框架设计(一)连接数据库 首先是连接数据库的操作,最好是单独写在一个模块里, 然后便于方便的调用,基于把connection连接放在__init__()方法里 然后分别定义D ...

  5. 基于Python的接口测试框架实例

    文章来源:http://www.jb51.net/article/96481.htm 下面小编就为大家带来一篇基于Python的接口测试框架实例.小编觉得挺不错的,现在就分享给大家,也给大家做个参考. ...

  6. 基于Python的接口测试框架

    分析 接口是基于HTTP协议的,那么说白了,就是发起HTTP请求就行了,对于Python来说比较简单.直接使用requests就可以很轻松的完成任务. 架构 整个框架是比较小的,涉及的东西也比较少,只 ...

  7. [转]基于Python的接口测试框架

    http://blog.csdn.net/wyb199026/article/details/51485322 背景 最近公司在做消息推送,那么自然就会产生很多接口,测试的过程中需要调用接口,我就突然 ...

  8. 基于Python接口自动化测试框架+数据与代码分离(进阶篇)附源码

    引言 在上一篇<基于Python接口自动化测试框架(初级篇)附源码>讲过了接口自动化测试框架的搭建,最核心的模块功能就是测试数据库初始化,再来看看之前的框架结构: 可以看出testcase ...

  9. 基于LoadRunner构建接口测试框架

    基于LoadRunner构建接口测试框架 http://www.docin.com/p-775544153.html

随机推荐

  1. 【Java】DateUtil(1)

    import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; impor ...

  2. Erlang-VM节点启动名冲突问题

    今天在启动聊天的ErlangVM后,在日志中发现错误信息: Protocol 'inet_tcp': the name chatserver@127.0.0.1 seems to be in use ...

  3. Eclipse慢慢学会的快捷键

    Java编辑器 添加单个import Ctrl+Shift+M Java编辑器 组织多个import Ctrl+Shift+O Ctrl+M切换窗口的大小 Ctrl+D删除当前行 ---------- ...

  4. WC2017游记

    Day0 到杭州之后出了点锅换了辆车,等了好久才开= =到宿舍发现路由器就在房门口,稳啊,过了一会儿就连不上了= =而且只有门口那个连不上,可以连上楼下的= =之后干了啥也忘了…… Day1 上午直接 ...

  5. angularJS ng-if的用法

    ng-if主要是用来判断是否显示,也可以做为而者选择其中一个的方法,满足判断条件ng-if="变量名" 显示,否者不显示,也可以用ng-if="!变量名"取反, ...

  6. Java的四大基础特性

    Java的四大基础特性 一.抽象 父类为子类提供一些属性和行为,子类根据业务需求实现具体的行为. 抽象类使用abstract进行修饰,子类要实现所有的父类抽象方法否则子类也是抽象类. 二.封装 把对象 ...

  7. Programming With Objective-C---- Encapsulating Data ---- Objective-C 学习(三) 封装数据

      Programming with Objective-C Encapsulating Data In addition to the messaging behavior covered in t ...

  8. 3.11-3.15 HDFS HA

    一.背景 1. Hadoop2.0之前,在HDFS集群中NameNode存在单点故障(SPOF).对于只有一个NameNode的集群, 若NameNode机器出现故障,则整个集群将无法使用,直到Nam ...

  9. (水题)洛谷 - P1036 - 选数

    https://www.luogu.org/problemnew/show/P1036 $n$ 才20的数据量,我当时居然还在想怎么分组组合,直接 $2^{20}$ 暴力搞就行了. $x_i $太大了 ...

  10. lightoj1169【DP】

    题意(来自大哥): 有两栋楼,左边一栋,右边一栋,层数从1-n,地面的标号为0,每一层有一个水果.有一只猴子在地面上,他现在要上到n层去,在第i层会吃掉水果花费一定时间. 猴子有两种方式从第i层到i+ ...