第一阶段:原始阶段
图一 金字塔模型
图二 纺锤模型
当然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软件测试网及相关内容提供者拥有内容的全部版权,未经明确的书面许可,任何人或单位不得对本网站内容复制、转载或进行镜像,否则将追究法律责任。