接口测试数据引发的接口测试代码改进(一)

发表于:2019-5-21 10:09

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:顾翔    来源:51Testing软件测试网原创

  第一阶段:原始阶段
  接口测试在DevOps推广之中的地位得到了显著的提高,对于接口测试相对于GUI自动化测试、基于单元测试自动化测试脱颖而出,以前测试金字塔模型(图一)逐渐被纺锤模型所取代。
                                        图一 金字塔模型
图二 纺锤模型
  当然JMeter、Postman也可以进行接口测试,但是它由于代码不可复用,越来越被类似于基于requests类Python代码取代了。基于requests类Python代码使接口测试变得非常的简单,我们只要组合get或者post参数,通过requests类的post或者get方法调用返回数据就可以实现,下面的代码展示了一个网站的登录功能的最原始接口测试代码。
   #最基本的操作
  import requests
  import unittest
  class login(unittest.TestCase):
  def setUp(self):
  self.correctusername="cindy"
  self.correctpassword="123456"
  self.discorrectusername="jerry"
  self.discorrectpassword="000000"
  self.url="http://localhost:8000/login_action/"
  #正确的用户名,错误的密码
  def test_correctusername_discorrectpassword(self):
  payload={"username":self.correctusername,"password":self.discorrectpassword}
  data = requests.post(self.url,data=payload)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("用户名或者密码错误" ,str(data.text))
  #错误的用户名,正确的密码
  def test_discorrectusername_correctpasswor(self):
  payload={"username":self.discorrectusername,"password":self.correctpassword}
  data = requests.post(self.url,data=payload)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("用户名或者密码错误" ,str(data.text))
  #错误的用户名,错误的密码
  def test_discorrectusername_discorrectpassword(self):
  payload={"username":self.discorrectusername,"password":self.discorrectpassword}
  data = requests.post(self.url,data=payload)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("用户名或者密码错误" ,str(data.text))
  #正确的用户名,正确的密码
  def test_correctusername_correctpassword(self):
  payload={"username":self.correctusername,"password":self.correctpassword}
  data = requests.post(self.url,data=payload)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("电子商务系统" ,str(data.text))
  if __name__=='__main__':
  #构造测试集
  suite=unittest.TestSuite()
  suite.addTest(login("test_correctusername_discorrectpassword"))
  suite.addTest(login("test_discorrectusername_correctpassword"))
  suite.addTest(login("test_discorrectusername_discorrectpassword"))
  suite.addTest(login("test_correctusername_correctpassword"))
  #运行测试集合
  runner=unittest.TextTestRunner()
  runner.run(suite)
  在这里分别验证了错误用户名,正确密码、正确用户名,错误密码、错误用户名,错误密码、正确用户名,正确密码四种情况。
  第二阶段:CSRF Token防护阶段
  为了防止CSRF的攻击,现在几乎所有的网站在POST请求的时候都加上了csrftoken,系统通过post参数中的一个hidden元素获取一个随机的由100个字符组成的串,在发送的时候与某个cookie值进行比较,如果二者相同,则进入下面的操作,否则这给出403异常页面。为了对这类产品进行接口测试,可以阅读源代码获得hidden元素名,通过抓包获得cookie名,然后通过post的cookie选项将hidden元素名对应的值和cookie名对应的值保持一致,即可实现。
   #csrftoken
  import requests
  import unittest
  def setUp(self):
  self.correctusername ="cindy"
  self.correctpassword ="123456"
  self.discorrectusername ="jerry"
  self.discorrectpassword ="000000"
  self.url="http://localhost:8000/login_action/"
  self.token = "RNF3Y04qFeJkMwCDsTMn4gfMcyfQ2vUjXbcENLADEFyCSRp1pBdezZKwHhlSwqgE"
  self.cookie = {"csrftoken":self.token}
  class login(unittest.TestCase):
  #正确的用户名,正确的密码
  def test_correctusername_correctpassword(self):
  payload={"username":self.correctusername,"password":self.correctpassword,"csrfmiddlewaretoken":self.token}
  data = requests.post(self.url,data=payload,cookies=self.cookie)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("电子商务系统" ,str(data.text))
  在这里,仅展示输入正确的用户名和正确的密码用例。通过语句payload={"username":self.correctusername,"password":self.correctpassword,"csrfmiddlewaretoken":self.token}设置hidden参数的值为self.token,通过data = requests.post(self.url,data=payload,cookies=self.cookie)(其中self.cookie在setUp中设置为self.cookie = {"csrftoken":self.token})也设置为self.token值。
  第三阶段:利用Excel封装数据,基于数据驱动的自动化测试。
  数据驱动是自动化测试的主流,在刚才的代码中测试数据是与测试代码在一起的,在这里我们通过excel表来维护数据,从而得到测试数据与测试代码的分离。建立一个excel表,内容如图三所示。
  图三 用Excel管理的数据
  在这里cindy/123456是数据库中存在的,而jerry/654321是数据库中不存在的。建立Util.py文件把对Excel表的操作封装在里面。代码如下。
   class util:
  def __init__(self):
  fname = 'data.xlsx'#Excel文件名
  self.filename = xlrd.open_workbook(fname)
  self.sheets = self.filename.nsheets
  self.sheet1 = self.filename.sheets()[0] #获取Excel第一个sheet
  #从Excel表中获取用户数据
  def get_user_data(self):
  self.row_datas = self.sheet1.row_values(0)#获取第一行
  correct_user = self.row_datas[0]#获取第一行第一列
  correct_password = self.row_datas[1] #获取第一行第二列
  row_datas = self.sheet1.row_values(1) #获取第二行
  discorrect_user = row_datas[0] #获取第二行第一列
  discorrect_password = row_datas[1] #获取第二行第二列
  parameter =[correct_user,correct_password,discorrect_user,discorrect_password]
  return parameter#以list的形式返回
  测试代码变为。
   #使用excel作为驱动
  import requests
  import unittest
  from Util import util
  class login(unittest.TestCase):
  def setUp(self):
  myutil=util()
  self.correctusername =myutil.get_user_data()[0]#以此读取Excel表中的参数
  self.correctpassword =myutil.get_user_data()[1]
  self.discorrectusername =myutil.get_user_data()[2]
  self.discorrectpassword =myutil.get_user_data()[3]
  self.url="http://localhost:8000/login_action/"
  self.token = "RNF3Y04qFeJkMwCDsTMn4gfMcyfQ2vUjXbcENLADEFyCSRp1pBdezZKwHhlSwqgE"
  self.cookie = {"csrftoken":self.token}
  调用get_user_data()方法以此得到测试数据,下面的测试实现同第二阶段。
  第四阶段:通过测试数据优化测试代码。
  (这部分是本文的关键),先来看一下图四。
  图四 关于登录操作测试脚本的测试数据问题
  这里讲的是接口测试,其实在GUI测试下同样适用。来看一下用户登录操作测试脚本失败,可能由两个以下原因:
  ?产品代码有问题:直接报bug;
  ?测试数据的问题:比如测试错误用户名,错误密码的时候,比如做ET的时候通过手工的方式已经有了这个用户名和错误密码的用户,再比如正确错误用户名,正确密码的时候,比如做ET的时候通过手工的方式已经将这条记录删除。
  我们现在考虑测试数据的问题,首先会考虑是否可以讲注册与登录一起测试,即先测试注册接口,然后用注册的信息进行登录,注册完毕直接删除数据库。这又产生两个问题:
  ?测试脚本是否有权限操作数据库?(在目前企业在测试环境下是允许的。)
  ?在微服务情况下,注册与登录分别作为单独一个services下如何实现。
  然后还可以考虑一种方法,在登录的时候进行检查,测试一个正常测试登录的测试数据,在登录之前先到数据库中检查,如果这个用户数据不存在,通过测试脚本把这条数据写入数据库中(写入之后给出日志信息,便于后续数据维护),然后在进行测试验证;测试一个异常登录的测试数据,在登录之前也先到数据库中检查,如果这个用户数据存在,则将数据库中数据删除(同样删除之后也要给出日志信息)(后续介绍的方法就是这种方法的实现)。也有一些大型企业通过数据管理服务来实现,如果获取的数据不符合测试业务,认为这是一条脏数据,从测试数据文件中删除然后取下一条。当然这种情况不会用Excel,XML文件来维护数据,而是通过测试数据数据库来为维护。另外会启动一个Jenkins Job。定时从测试数据库中获取数据个数,当数量小于某个阈值的时候自动生成批量的测试数据。现在来看下如何实现前一种方法。
  在Util类中建立check_user_existence(self,username,password,tag)方法,用来检查当前产品数据库中是否存在这个用户,然后通过tag(tag=0表示测试一个异常登录,tag=1表示测试一个正常测试登录)做相应的措施。
   import xlrd
  from xlutils.copy import copy
  from xml.dom import minidom
  import sqlite3,hashlib
  import os
  #检查用户数据是否存在,并且根据测试数据特殊处理
  def check_user_existence(self,username,password,tag):
  condition = "username='"+username+"' and password='"+self.md5(password)+"'"
  db =DB()
  db.connect()
  result = db.searchByCondition("goods_user",condition)
  #如果验证的是非法用户项,获得的数据已经在数据库中的处理办法,将这条数据从数据库中删除,输出信息
  if tag == 0:
  if len(list(result))!=0:
  condition = "username='" +username+"'"
  db.delete("goods_user",condition)
  print(username+","+password+" 由于数据问题被测试程序删除")
  #如果验证的是合法用户项,获得的数据不在数据库中,将这条数据插入到数据库中,输出信息
  elif tag == 1:
  if len(list(result))==0:
  value = "'"+username+"','"+self.md5(password)+"','"+username+"@126.com'"
  db.insert("goods_user(username,password,email)",value)
  print(username+","+password+" 由于数据问题由测试程序自动生成的数据")
  else:
  print("调入check_user_existence参数错误")
  db.close()
  为了便于后续的维护,封装了数据库操作和md5加密。
   #MD5加密
  def md5(self,mystr):
  if isinstance(mystr,str):
  m = hashlib.md5()
  m.update(mystr.encode('utf8'))
  return m.hexdigest()
  else:
  return ""
  …
  class DB:
  #构造函数,获得sqlite3数据库文件的位置
  def __init__(self):
  BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  self.url = BASE_DIR+"\\ebusiness\\db.sqlite3"
  #连接数据库连接
  def connect(self):
  self.con = con = sqlite3.connect(self.url)
  self.cur = self.con.cursor()
  #关闭数据库连接
  def close(self):
  self.cur.close()
  self.con.close()
  #通过主键查询数据库表中的内容
  def searchByid(self,tablename,id):
  return(self.cur.execute("select * from "+tablename+" where id="+id))
  #通过条件查询数据库
  def searchByCondition(self,tablename,Condition):
  return(self.cur.execute("select * from "+tablename+" where "+Condition))
  #向tablename表中插入数据values
  def insert(self,tablename,values):
  sql = "insert into "+tablename+" values ("+values+")"
  self.con.execute(sql)
  self.con.commit()
  #在tablename表,删除满足condtion条件的记录
  def delete(self,tablename,condition):
  sql = "delete from "+tablename+" where "+condition
  self.con.execute(sql)
  self.con.commit()
  测试代码最后为。
   #使用excel作为驱动,进一步优化
  import requests
  import unittest
  from Util import util
  class login(unittest.TestCase):
  def setUp(self):
  self.myutil=util()
  self.correctusername =self.myutil.get_user_data()[0]
  self.correctpassword =self.myutil.get_user_data()[1]
  self.discorrectusername =self.myutil.get_user_data()[2]
  self.discorrectpassword =self.myutil.get_user_data()[3]
  self.url="http://localhost:8000/login_action/"
  self.token = "RNF3Y04qFeJkMwCDsTMn4gfMcyfQ2vUjXbcENLADEFyCSRp1pBdezZKwHhlSwqgE"
  self.cookie = {"csrftoken":self.token}
  #正确的用户名,错误的密码
  #登录之前,确保数据库中没有这条数据
  def test_correctusername_discorrectpassword(self):
  username = self.correctusername
  password = self.discorrectpassword
  self.myutil.check_user_existence(username,password,0)
  payload={"username":username,"password":password,"csrfmiddlewaretoken":self.token}
  data = requests.post(self.url,data=payload,cookies=self.cookie)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("用户名或者密码错误",str(data.text))
  #错误的用户名,正确的密码
  def test_discorrectusername_correctpassword(self):
  username = self.discorrectusername
  password = self.correctpassword
  self.myutil.check_user_existence(username,password,0)
  payload={"username":username,"password":password,"csrfmiddlewaretoken":self.token}
  data = requests.post(self.url,data=payload,cookies=self.cookie)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("用户名或者密码错误" ,str(data.text))
  def test_discorrectusername_discorrectpassword(self):
  #错误的用户名,错误的密码
  username = self.discorrectusername
  password = self.discorrectpassword
  self.myutil.check_user_existence(username,password,0)
  payload={"username":username,"password":password,"csrfmiddlewaretoken":self.token}
  data = requests.post(self.url,data=payload,cookies=self.cookie)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("用户名或者密码错误",str(data.text))
  #正确的用户名,正确的密码
  def test_correctusername_correctpassword(self):
  username = self.correctusername
  password = self.correctpassword
  self.myutil.check_user_existence(username,password,1)
  payload={"username":username,"password":password,"csrfmiddlewaretoken":self.token}
  data = requests.post(self.url,data=payload,cookies=self.cookie)
  #验证返回码
  self.assertEqual("200",str(data.status_code))
  #验证返回内容
  self.assertIn("电子商务系统" ,str(data.text))
  if __name__=='__main__':
  #构造测试集
  suite=unittest.TestSuite()
  suite.addTest(login("test_correctusername_discorrectpassword"))
  suite.addTest(login("test_discorrectusername_correctpassword"))
  suite.addTest(login("test_discorrectusername_discorrectpassword"))
  suite.addTest(login("test_correctusername_correctpassword"))
  #运行测试集合
  runner=unittest.TextTestRunner()
  runner.run(suite)
  
     版权声明:本文出自51Testing会员投稿,51Testing软件测试网及相关内容提供者拥有内容的全部版权,未经明确的书面许可,任何人或单位不得对本网站内容复制、转载或进行镜像,否则将追究法律责任。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号