项目背景:某图书商城网站项目,已完成一批用户信息处理接口的开发,首先需要对这些接口使用postman工具进行首次测试,然后设计python脚本完成自动化回归测试,最后使用unittest框架完成接口测试框架的设计与开发。
使用接口自动化+python+unittest技术完成。
目录
一、了解接口测试
1.接口的主要组成要素:
请求地址、请求方法、接口参数、接口返回值
请求方法主要有四种:
GET:请求指定的页面信息,并返回实体主体。
POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件),数据被包含在请求体中,POST 请求可能会导致新的资源的建立和/或已有资源的修改。
PUT:从客户端向服务器传送的数据取代指定的文档的内容。
DELETE:请求服务器删除指定的页面。
2.为什么要进行接口测试
3.接口测试与其他自动化测试区别
4.了解HTTP协议
①组成:请求行、请求头、空行、消息主体
-
HTTP 请求格式:
-
HTTP 响应格式:
②http状态码
③http协议与https协议:
- HTTPS 协议需要到 CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。(以前的网易官网是http,而网易邮箱是 https 。)
- HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
- HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- HTTP 的连接很简单,是无状态的。HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传
④协议与接口的关系:通过fiddler抓包工具抓取http协议,分析数据包,来获取接口测试的相关内容。
二、开展接口测试
进行接口测试的需求分析、测试用例设计,并用postman工具完成首次测试
1.步骤:
2、确定接口测试工作目标
①通过阅读开发的接口设计文档,确定测试范围:
本次测试接口共有11个,每个接口需要列出请求方法、接口参数及类型、请求地址、响应信息。
以用户登录接口为例,
②根据项目紧急程度及要求,确定测试标准:
③根据测试标准,完成接口测试文档的设计:
将接口名、URL、请求方法、请求参数等汇总至excel表格
3.postman测试
使用postman+fiddler工具根据接口测试文档对接口进行逐一测试,填写测试结果
4.针对异常接口,与开发确认并提交bug单,如都无异常,可开展后续接口自动化回归测试。
三、接口自动化测试
1.独立接口测试脚本
①关键语句
利用requests发送带有URL及接口参数信息的请求,使用response接收;
并与预期结果进行比对,判断测试用例是否通过。
url = "http://127.0.0.1:8080/jwshoplogin/user/login.do"
userinfo = {"username":"张伟3",
"password":"123456"}
response = requests.post(url,data=userinfo).text
msg=response.find("登录成功")
if msg>0:
print("测试通过")
else:
print("测试不通过")
②V1.0版本:
将传入参数及预期结果放入CSV文件,利用CSV文件读写,完成登录接口的测试数据读取与测试结果写入
url = "http://127.0.0.1:8080/jwshoplogin/user/login.do"
userinfo = {}
file = open("11.csv","r")
file2 =open("result.csv","w")
table = csv.reader(file)
for row in table:
userinfo["username"]=row[0]
userinfo["password"] = row[1]
response = requests.post(url, data=userinfo).text
msg=response.find(row[2])
if msg>0:
print("测试通过")
file2.write(row[0]+","+row[1]+","+row[2]+","+"测试通过"+"\n")
else:
print("测试不通过")
file2.close()
- 测试数据:
- 测试结果:
③V2.0版本:
将URL、接口名称、传入参数及预期结果 全部放入CSV文件,利用CSV文件读写,完成所有独立接口的测试数据读取与测试结果写入。
注意:URL、接口名称、预期结果属于共同参数,比较好处理,可以写入CSV文件的前几列;
传入参数每个接口不同,需要在CSV中添加一列用于控制传入参数读取的循环次数
- 测试数据:以注册、登录、校验用户名或者邮件这三个接口为例
- 脚本:
import requests
import csv
class interfaceV4():
def userinerface(self,url,userinfo,expresult,interfacename):
result = {}
response = requests.post(url, data=userinfo).text
r = response.find(expresult)
result["接口实际返回值"]=str(response)
if r > 0:
print(interfacename+"测试通过")
result["测试结果"]="测试通过"
else:
print(interfacename+"测试不通过")
result["测试结果"] = "测试不通过"
return result
def testjieguo(self,interfacename,filename,result):
file2 = open(filename, "a")
for key, value in result.items():
print(key, value)
file2.write(str(interfacename)+","+(key) + "," + str(value) + ",")
file2.write("\n")
file2.close()
if __name__ == '__main__':
obj =interfaceV4()
filename ="result4.csv"
userinfo ={}
result = {}
file1 = open("44.csv","r")
file2 = open("result4.csv", "a")
table = csv.reader(file1)
for row in table:
url = row[1]
expresult = row[3]
interfacename =row[5]
j = int(row[6])
for i in range(7, j * 2 + 6, 2):
userinfo[row[i]] = row[i + 1]
result = obj.userinerface(url,userinfo,expresult,interfacename)
userinfo = {}
obj.testjieguo(interfacename,filename,result)
- 测试报告:
2.接口联调脚本:
①业务分析:哪些接口需要进行联调测试
以 注册-登录-忘记密码-提交密保问题答案-修改密码-登录 为例进行接口联调脚本设计
②免登陆:
需要考虑网站使用的何种身份验证技术(cookie、session、token),首先需要通过登录获取验证信息,后续接口测试中将验证信息放入字典数据中进行传入,即可绕过登录。
cookies:cookie是以键值对进行表示的(key-value),保存在客户端
session:不同的用户访问服务端的时候会在session对象中存储键值对,“键”用来存储开启这个用户信息的“钥匙”,在登录成功后,“钥匙”通过cookie返回给客户端,客户端存储为sessionId记录在cookie中。当客户端再次访问时,会默认携带cookie中的sessionId来实现会话机制。
session是基于cookie的。
有效期:可以自行通过expires进行具体的日期设置,如果没设置,默认是关闭浏览器时失效。
有效范围:当前域名下有效。所以session这种会话存储方式方式只适用于客户端代码和服务端代码运行在同一台服务器上(前后端项目协议、域名、端口号都一致,即在一个项目下)
token:浏览器访问Web服务器后认证成功后生成Token并返回给客户端,客户端浏览器后续的请求都会把这个Token带到服务器端去验证,以此判定请求是否合法。
Token是放在客户端存储的,采用了时间换空间策略,它也是无状态的,所以在分布式环境中应用广泛
具体实现:
1.调用登录接口获取cookies中的JSESSIONID:
response = requests.post(url, data=userinfo)
self.sessionID = dict(response.cookies)['JSESSIONID']
2.调用更新用户接口时将获取的JSESSIONID传入cookies中:
session = {"JSESSIONID": self.sessionID}
response = requests.post(url, data=userinfo, cookies=session).text
③接口联调的注意事项:
在接口联调中,上一个接口的返回值可能是下一个接口的传入参数,需要进行返回值及接收;或者定义为self属性。
如提交密保问题接口的返回值是修改密码接口的传入参数
④接口联调脚本编写
与单接口类似,先实现一组固定参数的成功调用,然后进行参数化,利用CSV文件进行测试数据的读取和写入。
global token
class mulforget(unittest.TestCase):
global token
def test_case1(self):
url = "http://127.0.0.1:8080/jwshoplogin/user/register.do"
userinfo = {}
path = os.getcwd()
p1 = os.path.abspath(os.path.dirname(path) + os.path.sep + ".")
fpath = p1 + "\\testdata\mul_interface\\test_mulforget.csv"
file1 = open(fpath, "r")
table = csv.reader(file1)
for row in table:
userinfo["username"] = row[0]
userinfo["password"] = row[1]
userinfo["email"] = row[2]
userinfo["phone"] = row[3]
userinfo["question"] = row[4]
userinfo["answer"] = row[5]
# print(userinfo)
response = requests.post(url, data=userinfo).text
self.assertIn(row[6], response)
break
def test_case2(self):
url = "http://127.0.0.1:8080/jwshoplogin/user/login.do"
userinfo = {}
path = os.getcwd()
p1 = os.path.abspath(os.path.dirname(path) + os.path.sep + ".")
fpath = p1 + "\\testdata\mul_interface\\test_mulforget.csv"
file1 = open(fpath, "r")
table = csv.reader(file1)
n=0
for row in table:
if n>0 and n<2:
userinfo["username"]=row[0]
userinfo["password"] = row[1]
# print(userinfo)
response = requests.post(url, data=userinfo).text
self.assertIn(row[2], response)
n=n+1
def test_case3(self):
url = "http://localhost:8080/jwshoplogin/user/forget_get_question.do"
userinfo = {}
path = os.getcwd()
p1 = os.path.abspath(os.path.dirname(path) + os.path.sep + ".")
fpath = p1 + "\\testdata\mul_interface\\test_mulforget.csv"
file1 = open(fpath, "r")
table = csv.reader(file1)
n = 0
for row in table:
if n > 1 and n < 3:
userinfo["username"] = row[0]
s = requests.session()
response = s.post(url, data=userinfo).text
# print(response)
self.assertIn(str(row[1]), str(response))
n = n + 1
def test_case4(self):
url = "http://localhost:8080/jwshoplogin/user/forget_check_answer.do"
userinfo = {}
path = os.getcwd()
p1 = os.path.abspath(os.path.dirname(path) + os.path.sep + ".")
fpath = p1 + "\\testdata\mul_interface\\test_mulforget.csv"
file1 = open(fpath, "r")
table = csv.reader(file1)
n = 0
for row in table:
if n > 2and n < 4:
userinfo["username"] = row[0]
userinfo["question"] = row[1]
userinfo["answer"] = row[2]
# print(checkinfo)
# s = requests.session()
response = requests.post(url, data=userinfo).text
# print(response)
dic = {}
dic = eval(response)
globals()["token"]=dic["data"]
print(token)
# token = dic["data"]
#
# print(token)
self.assertIn(row[3], response)
n = n + 1
def test_case5(self):
url = "http://localhost:8080/jwshoplogin/user/forget_reset_password.do"
userinfo = {}
path = os.getcwd()
p1 = os.path.abspath(os.path.dirname(path) + os.path.sep + ".")
fpath = p1 + "\\testdata\mul_interface\\test_mulforget.csv"
file1 = open(fpath, "r")
table = csv.reader(file1)
n = 0
for row in table:
if n > 3 and n < 5:
userinfo["username"] = row[0]
userinfo["passwordNew"] = row[1]
userinfo["forgetToken"] = str(token)
print(userinfo)
response = requests.post(url, data=userinfo).text
# print(response)
self.assertIn(row[2], response)
n=n+1
def test_case6(self):
url = "http://127.0.0.1:8080/jwshoplogin/user/login.do"
userinfo = {}
path = os.getcwd()
p1 = os.path.abspath(os.path.dirname(path) + os.path.sep + ".")
fpath = p1 + "\\testdata\mul_interface\\test_mulforget.csv"
file1 = open(fpath, "r")
table = csv.reader(file1)
n=0
for row in table:
if n>4 and n<6:
userinfo["username"]=row[0]
userinfo["password"] = row[1]
# print(userinfo)
response = requests.post(url, data=userinfo).text
self.assertIn(row[2], response)
n=n+1
if __name__ == '__main__':
path = os.getcwd()
p1 = os.path.abspath(os.path.dirname(path) + os.path.sep + ".")
suite = unittest.TestSuite()
suite.addTest(mulforget("test_case1"))
suite.addTest(mulforget("test_case2"))
suite.addTest(mulforget("test_case3"))
suite.addTest(mulforget("test_case4"))
suite.addTest(mulforget("test_case5"))
suite.addTest(mulforget("test_case6"))
filename=p1+".\\testresult\mul_interface\\report_test_mulforget.html"
file=open(filename,"wb")
runner=HTMLTestRunner(stream=file, verbosity=1, title="自动化测试报告", description="更新用户信息接口", tester="周卓阳")
runner.run(suite)
file.close()
四、接口测试框架设计
使用unittest框架进行接口测试框架的设计
1.框架目录建立
- 共分为5层
- 测试框架的执行过程
2.框架脚本的研发
方法一:①重构测试脚本
方法二:②改写现有脚本
注意:
- 引入unittest框架、并继承unittest框架,并修改测试方法名称,都要以test开头
- 每改写完成一个,要单独调试
3.设计及实现框架驱动程序
配置层及驱动层属于测试框架中最重要的部分,决定了测试用例如何被组织及执行
1.配置层config.csv设计
将unittest框架的脚本名称及路径都放入CSV文件中,同时加上是否运行及执行顺序这两列。
功能:可以由config文件中的是否运行列来决定是否执行测试脚本,并可以按照执行顺序列决定用例执行顺序。
2.驱动脚本编写
配置层csv文件设计好后,接下来就是编写驱动脚本来读取config.csv文件并按照设计的功能进行调用执行
①执行顺序的实现:
将csv文件读取后加入字典,通过sorted对执行顺序(命名为num)进行排序
dicn=sorted(listd,key=operator.itemgetter("num"))
②是否运行的实现:
将是否运行的“yes“或者“no”状态作为key值也加入字典,value值用“state”表示,加入if语句判断,如果读出来是yes便执行
dic["state"] = row[2]
state =content[1]
if state=="yes":
③将找到的测试用例用unittest的discover方法加载,然后用run方法执行
suite = unittest.defaultTestLoader.discover(dir, fname) unittest.TextTestRunner().run(suite)
④驱动文件脚本:先进行顺序判断再进行是否执行判断
if __name__ == '__main__':
file =open("F:\interface\interface_project\ceshikuangjia\config\config1.csv","r")
table =csv.reader(file)
#定义空字典接收每行读取出来的数据
dic={}
# 定义空列表接收每行的字典,形成列表字典
listd=[]
line=0
for row in table:
#跳过首行
if line>0:
#每读一行需要清空一次字典
dic={}
#文件路径:文件名
dic[row[1]]=row[0]
#state状态:是否运行
dic["state"] = row[2]
#num:执行顺序
dic["num"]=int(row[3])
# print(dic)
line=line+1
if dic!={}:
#如果字典不为空,加入到列表中
listd.append(dic)
# print(listd)
#将列表字典按num的值进行排序
dicn=sorted(listd,key=operator.itemgetter("num"))
# print(dicn)
for i in range(0,line-1):
n=0
#按排好的顺序逐个读取列表中的字典
for content in dicn[i].items():
if n==0:
# print(content)
fname=content[0]
dir=content[1]
print("fname",fname," dir",dir)
# n = n + 1
if n==1:
state =content[1]
# print(state)
if state=="yes":
suite = unittest.defaultTestLoader.discover(dir, fname)
unittest.TextTestRunner().run(suite)
n=n+1
4.总结
1.框架概览:
2.框架的价值
最终只需要调整框架配置文件的一些参数,不需要人工干预测试脚本的执行