导语
上篇总结了独立接口业务的分析过程,本次总结接口联调业务的分析过程,仍然尝试分解需求任务,采用多版本迭代,在实现需求的前提下再去做代码优化。
一、接口联调业务分析
对所有的接口需要有一个全局的认识:接口名称、接口功能、接口参数、接口返回值,可以将这些内容列一个表格,这样就将抽象的工作任务转化成了具体的工作成果物,对于接口的相关情况也更明晰。
分析接口联调的业务,本次以修改密码为例:用户注册-->用户登录-->忘记密码-->提交密保问题答案-->修改密码-->用户登录。图1为简单整理的接口内容,未展示的接口也如此整理:

二、使用postman工具进行测试
可以先使用postman工具先过一遍这个接口联调业务的情况,可以更熟悉相关内容,这里仅说明两个较为特殊的接口情况。


图2为提交密保答案接口send以后的情况,图3为修改密码接口send以后的情况,可以看出,提交密保答案的一个返回数据是作为修改密码的一个传参,这在后续编写脚本时需注意。
在这里简单的介绍一下cookie、session、token:
1、cookie
(1)通常,登录后,在浏览器端生成一个文件,保存在浏览器的客户端;
(2)一般会存储用户的身份信息;
(3)可以删除,删除后,重新登录可以再次生成;
(4)有些系统也会通过cookie记录一些用户的操作习惯;
2、session
(1)登录后,服务器端发送一个随机的ID值,来进行用户身份的识别;
(2)有时效性,在代码中进行设计,一般30分钟;
(3)只支持一个应用服务器有效;
3、token
(1)登录后,服务器端发送一个token令牌;
(2)有时效性;
(3)可以支持多平台访问
三、编写接口联调脚本
1、V1.0版本
一般类的设计有两种方式,一种是一个接口对应一个类,接口比较少时可以采用,另一种是一个类对应多个测试方法,一个测试方法进行一个接口测试(本次采用这个)。本次为V1.0版本,传入固定的一组数据,且仅在代码中打印接口测试的结果。
注:提交密码问题答案的接口,返回值是下一个接口(修改密码)其中的一个参数(forgetToken)。
一般会采用画流程图的方式来分析业务,以下简单的画了接口联调的业务流程、设计类图、以注册接口举例的详细设计流程图,其他功能类似,就不多做描述。

以下仅显示answer_test()和changeword_test(token)方法定义的写法,显示需要返回值和传值的情况,其他的结构与其他方法结构类似,不多加描述,代码如下:
# 提交密保答案
def answer_test(self):
url = "http://localhost:8080/.../forget_check_answer.do"
userinfo = {
"username": "小绿",
"question": "喜欢的水果",
"answer": "苹果"
}
response = requests.post(url, data=userinfo).text
# print(response)
# print(type(response))
# 字符串类型转化为字典类型
dic = {}
dic = eval(response)
token = dic["data"]
return token
result = response.find("data")
if result > 0:
print("用户提交密保问题答案接口测试通过")
else:
print("用户提交密保问题答案接口测试失败")
# 修改密码
def changeword_test(self, token):
url = "http://localhost:8080/.../forget_reset_password.do"
userinfo = {
"username": "小绿",
"passwordNew": "123456",
"forgetToken": token
}
response = requests.post(url, data=userinfo).text
# print(response)
result = response.find("修改密码成功")
if result > 0:
print("用户修改密码接口测试通过")
else:
print("用户修改密码接口测试失败")代码中有一些被注释的print语句,是调试过程中为了更直观的查看脚本编写是否正确,这里说个小tip,当发现代码报错是类型转换的问题,可以通过print(type())来查看相关类型。将方法都定义好以后,最后再在main中调用即可,V1.0版本最后的代码结构如下:

2、V2.0版本
在上个版本中,其实已经基本实现了接口联调,但是可以看出,很多方法的结构是类似的,每个方法中均需要定义一个URL,一个userinfo数据,然后比对响应结果,那是不是可以优化一下结构,设计一个通用的方法,需要传入3个参数,是不是同样可以实现相同的功能呢?
观察发现,注册、登录、忘记密码三个接口,不需要返回值,也不需要特殊参数时,接口请求方式也一致,这样可以合并在一起,定义成一个方法,类图优化如下:

优化后的代码如下所示(此处仅显示注册接口相关参数,其他接口类似):
import requests
# 定义一个测试类
class forgetpassword_flow_v2():
def userint_test(self, url, userinfo, expresult, interface):
response = requests.post(url, data=userinfo).text
result = response.find(expresult)
if result > 0:
print(interface + "测试通过")
else:
print(interface + "测试失败")
if __name__ == '__main__':
url = "http://localhost:8080/.../register.do"
userinfo = {
"username": "小绿",
"password": "123456",
"email": "xiaolv@126.com",
"phone": "18120121134",
"question": "喜欢的水果",
"answer": "苹果"
}
expresult = "注册成功"
interface = "用户注册接口"
objflow = forgetpassword_flow_v2()
objflow.userint_test(url, userinfo, expresult, interface)
这样优化以后,提高了测试脚本的复用性,只需要维护需要传入的测试数据即可,还方便后续接口测试工作的扩展及调用。那如何在不同的Python文件中进行类的实例化和调用呢?以忘记密码这个参数较少的接口为例,代码如下:
import requests
from com_interface_test.forgetword_test_flow_v2 import forgetpassword_flow_v2
if __name__ == '__main__':
objflow = forgetpassword_flow_v2()
url = "http://localhost:8080/.../forget_get_question.do"
userinfo = {
"username": "小绿"
}
expresult = "喜欢的水果"
interface = "忘记密码接口"
objflow.userint_test(url, userinfo, expresult, interface)
只需要从其他脚本中导入已经写好的类,便可以在main中去调用所需要的方法。从这个例子中更加可以看出,优化后的代码结构,适用于多人使用,可以同时编写不同接口的测试脚本。
3、V3.0版本
从以上版本中,可以看出,仍有许多可优化点,比如说,把相关的接口测试参数放入CSV文件中,程序通过读取CSV文件数据,这样就不用在脚本中重复的写每个接口的传参,利于后期的维护工作。但是观察接口内容可知,不同的接口需要的传参数量是不同的,对于传参数量相同的情况,在上一篇探索接口自动化(一)--独立接口(Python)中已经说明过,本次不再多加描述,主要来探索传参数量不同的情况,这里可以先做技术实验。
先准备一个CSV文件,如下图所示(图7):

注意中间框选的数字列,是人为数出来后面有几个参数,方便后续循环读取。这里需要解决的难题有两个,一个是参数数量不同,另一个是之前脚本中用户参数是定义在字典数据userinfo中的,这样不仅要读取出来,还需要放入到字典数据中。先观察csv文件中每列所需值的下标规律,为了方便解释,可以观察下图(图8)所示:

注意:比如说for i in range(7,11),对于Python而言,11是取不到的,那么根据上图规律,正好可以得出循环的范围,这里也体现出人为数出参数个数以后,编写脚本也更加便捷,技术实验的脚本如下:
import csv
file = open("test1.csv", "r")
table = csv.reader(file)
userinfo = {}
for ele in table:
url = ele[1]
expresult = ele[3]
interface = ele[5]
print("url=", url, " ", "expresult=", expresult, " ", "interface=", interface)
j = ele[6]
print(type(j))
for i in range(7, 2 * j + 7, 2):
userinfo[ele[i]] = ele[i + 1]
userinfo = {}
print(userinfo)执行以后发现报错了,报错内容为:for i in range(7, 2 * j + 7, 2):TypeError: must be str, not int。这里就可以如上说的小tip,打印j的类型,发现是str,在循环范围中就会报错,这样将j转换为int类型即可(j=int(ele[6]))。从技术实验中可以总结出,对于参数数量不同的情况,只需要去找出循环规律,即可循环读取,然后将取出的数据再一个个放入已经声明的字典数据userinfo中。技术实验成功后,与之前的脚本进行整合,即可实现V3.0的脚本优化工作,代码如下:
import requests, csv
# 定义一个测试类
class forgetpassword_flow_v3():
def userint_test(self, url, userinfo, expresult, interface):
response = requests.post(url, data=userinfo).text
result = response.find(expresult)
if result > 0:
print(interface + "测试通过")
else:
print(interface + "测试失败")
if __name__ == '__main__':
objflow = forgetpassword_flow_v3()
file = open("test1.csv", "r")
table = csv.reader(file)
userinfo = {}
for ele in table:
url = ele[1]
expresult = ele[3]
interface = ele[5]
j = int(ele[6])
for i in range(7, 2 * j + 7, 2):
userinfo[ele[i]] = ele[i + 1]
objflow.userint_test(url, userinfo, expresult, interface)
userinfo = {}
4、V4.0版本
代码结构优化后,来解决最后的问题,将脚本执行后输出测试报告,内容包括接口名称、实际返回值、测试结论。上一篇探索接口自动化(一)--独立接口(Python)中也提到了如何用程序写入数据到CSV文件中,但是这次的情况不太一样,回头再看一下代码(图9):

从图9可以看出,测试报告需要response的值和“测试通过”结论,且需要分开写入文件中,用return的方法不太可行,那么可以采用字典数据接收response和测试结论,然后再写入CSV文件中输出测试报告文件,这也是本次的技术难点,可以先做一个小试验,如何将字典数据的内容写入测试报告中?写入的方式有两种,覆盖写入(w)和追加写入(a),对于本次业务而言,w方式只能就放在main中,a方式可以单独写一个方法,其实代码编写方式基本一致,只是适用场景不同。
最先想到的方法如下(图10):

程序报错了,根据报错内容,做一下类型转换,再运行一下,如下(图11):

程序运行成功了,但是这样还是不符合需求,只需要写入“接口名”字样即可,这样也发现了这个keys是一个集合,那么是否可以用循环去单独取每一个值呢?修改如下(图12):

程序运行后,发现确实取出来了,但是写在了一个单元格里,后来通过查找资料得知,其实Python里有一个方法可以很简单的处理此类问题,如下(图13):

程序运行后,这样就离预期效果很接近了,如果想要换行,就在末尾加"\n",如果仍想显示在一行,就加个“,”,至此,绕了一圈以后,技术实验终于成功完成。
最终的脚本实现,这里用a方法来实现,代码如下:
import requests, csv
# 定义一个测试类
class forgetpassword_flow_v4():
def userint_test(self, url, userinfo, expresult, interface):
resultdata = {}
response = requests.post(url, data=userinfo).text
resultdata["实际结果"] = response
result = response.find(expresult)
if result > 0:
resultdata["测试结论"] = "测试通过"
else:
resultdata["测试结论"] = "测试失败"
return resultdata
def result_report(self, interface, resultdata):
report = open("testreport.csv", "a")
for key, value in resultdata.items():
report.write(interface + "," + key + "," + value + ",")
report.write("\n")
report.close()
if __name__ == '__main__':
objflow = forgetpassword_flow_v4()
resultdata = {}
file = open("test1.csv", "r")
table = csv.reader(file)
userinfo = {}
for ele in table:
url = ele[1]
expresult = ele[3]
interface = ele[5]
j = int(ele[6])
for i in range(7, 2 * j + 7, 2):
userinfo[ele[i]] = ele[i + 1]
resultdata = objflow.userint_test(url, userinfo, expresult, interface)
objflow.result_report(interface, resultdata)
userinfo = {}
四、接口联调总结
本次总结了接口联调业务的分析过程,接口分析-->postman工具测试接口-->拆分需求,分版本迭代完成脚本-->优化脚本(将测试数据存入CSV文件中,方便维护,并输出CSV文件的测试报告)。
在优化脚本时,要注意脚本的请求方法以及传参和返回值的特点,基本一致才可以进行脚本结构的优化,否则还是要单独编写。对于新手而言,技术不熟练,可以先进行技术实验,再进行脚本研发,先进行类及方法的设计,再进行具体实现,这样逐个编写,逐个调试,就能由简入繁,逐步实现目标需求。
成长之路,道阻且长,后续会继续总结自己平时遇到的一些问题,加油!