在整体的测试效率而言,API测试技术是提升测试效率最有效的手段之一,因为它的执行效率是非常高的,另外一点就是前后端的分离开发的模式,也需要我们更多的精力和时间投入到API的测试技术以及API的测试技术在企业的落地和应用。当然,这仅仅是功能层面的,还需要考虑非功能的点,比如队列,调度机制,服务的性能测试,稳定性的因素,这些是非常多的。在本篇文章中,只单纯的考虑API测试技术中关于关联的解决思路和案例应用。API测试的核心,其实并不在于单个API的测试,单个API无法保障业务的覆盖度,所以我们更多需要结合业务场景来测试这些点,但是一旦结合具体的业务场景,也就涉及到关联的思路,所谓关联,其实我们可以理解为上个API的输出是下个API的输入部分。下面结合主流的测试工具以及代码来演示这部分的具体解决方案和案例实战。
API测试中业务关联解决方案
下面的源代码主要显示的是一个订单的微服务,我们需要在登录的情况下才能够查看订单的明细数据,也就是说在登录成功后,调用订单明细的接口才可以返回订单明细的数据,如果未登录的情况下,那么就返回401的的错误信息。具体流程可以详细的描述为:登录成功,返回登录成功后的token的信息,在访问下个接口的时候需要带上登录成功返回的token值的信息,才可以正常的访问订单明细,而且有一点需要特别注意的是每次登录成功后返回的token都是随机的字符串,并且需要保持访问接口的token与登录成功返回的token值一致,其实解决问题的思路是非常简单的,就是使用关联的思路来进行解决。案例源代码具体如下:
代码语言:javascript复制from flask import Flask,make_response,jsonify,abort,request
from flask_restful import Api,Resource
from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp
app=Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'super-secret'
api=Api(app=app)
orders=[
{'id':1,'author':'wuya','bookName':'Python接口自动化测试实战','done':True,'payType':'支付宝','price':69.9},
]
class User(object):
def __init__(self, id, username, password):
self.id = id
self.username = username
self.password = password
def __str__(self):
return "User(id='%s')" % self.id
users = [
User(1, 'wuya', 'asd888')
]
username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}
def authenticate(username, password):
user = username_table.get(username, None)
if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
return user
def identity(payload):
user_id = payload['identity']
return userid_table.get(user_id, None)
jwt = JWT(app, authenticate, identity)
class Order(Resource):
decorators=[jwt_required()]
def get(self):
return jsonify({'status':0,'msg':'ok','datas':orders})
api.add_resource(Order,'/v1/order')
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0')
PostMan中关联解决思路
在清楚了业务场景以及思路后,下来具体使用PostMan测试工具来演示下它的应用,PostMan是非常主流的API测试工具,在日常的研发过程中测试开发和开发同学都会使用到这个工具来验证接口的准确性。下面详细的演示下针对关联的解决思路。
PostMan中获取Token
首先我们需要在PostMan测试工具中获取token值的信息,访问的接口地址信息为/auth,具体如下:
在如上中可以看到,请求成功后,会返回access_token值的信息,下来需要思考的点就是在PostMan测试工具中在tests中获取响应数据中的access_token的值,并且在tests中定义一个变量来存储获取的access_token的值,在tests的中可以使用JS的代码来获取响应的数据,也就是通过JSON.parse()把JSON字符串转位JSON对象,然后定义一个变量来存储获取到的JSON对象,然后在这个变量中再使用PostMan中的environment来定义一个变量来存储获取到的access_token的值,具体过程如下:
代码语言:javascript复制//把JSON字符串转为JSON的对象
var jsonData=JSON.parse(responseBody)
console.log(jsonData)
具体在PostMan的信息如下展示:
在如上的截图中我们可以看到已经获取到了响应的数据,下来就是在响应数据中分离出access_token的值,并且定义一个变量来存储获取到的access_token,更新后的JS代码为:
代码语言:javascript复制//把JSON字符串转为JSON的对象
var jsonData=JSON.parse(responseBody)
console.log(jsonData)
//定义token变量来存储分离出的access_token
pm.environment.set("token", jsonData.access_token);
//获取定义的变量值校验是否获取
console.log(pm.environment.get("token"))
整体在截图信息如下所示:
PostMan中调用变量
下来就是调用定义的变量,在调用中,一般是在请求头的Authorization中,也就是说请求头中,key是Authorization,而具体的value值就是:jwt变量值,有一点需要特别的注意,就是jwt与变量之间是有空格的。在PostMan中调用变量的方式是具体为:{{变量}}。下面我们来调用订单详情的接口信息,具体如下:
在如上可以看到,返回的结果信息是401,并不是我们期望的协议状态码是200,具体详细的错误信息可以在PostMan的console中看到,这主要主要需要关注的是请求头的部分,具体如下:
通过如上可以看到,我们定义的变量还是变量,应该是具体是变量值才对,具体导致这样的问题其实是很简单的,也就是说我们并没有把登录的接口和查看订单详细的接口关联起来,所以导致这样的问题。
PostMan中Collections解决关联
在如上的问题中我们可以很清晰的知道问题所在,也就是说接口测试用例需要有顺序的执行,再说的简单点就是先执行登录的接口再接着执行订单详情的接口,这样二者之间就能够关联起来。在PostMan可以通过Collections很轻松的来解决这部分的问题,Collections可以理解为测试套件,或者是测试容器,在这个容器里面可以存储很多的测试用例,所以容器它解决了两个问题,具体可以总结为:
- 可以把所有的测试用例添加到Collections中进行批量的执行,这样解决了单个测试用例执行的效率
- 解决了API测试中业务关联的问题
下来创建名为订单的Collections,然后把登录的接口和订单详情的接口添加到Collectios中,具体添加后的信息为:
我们把测试用例添加到Collections中,下来执行订单的Collectios,也就执行了该Collections中所有的测试用例,执行后的结果信息具体如下所示:
在如上中,可以很清晰的看到,在订单详情接口的请求头中,变量token的值完整的进行了替换,当然接口返回的协议状态码不再是401,而是200,下面具体显示整体的执行结果信息:
JMeter关联解决思路
下面使用另外一款主流的测试工具也就是JMeter来详细的演示下这部分的具体应用,JMeter测试工具可以应用于多种协议的API测试,当然也可以应用于性能测试工具,可以说它是一款轻量级的API测试工具和性能测试工具。
JMeter中获取变量值
首先在JMeter的测试工具中创建一个线程组,以及添加登录的接口信息,具体展示信息如下所示:
执行结果后,获取到响应数据,在PostMan的测试工具中,可以通过后置处理器的正则表达式或者是JSON提取器获取到响应数据,使用JSON提取器的前提是需要安装JSON的插件(在这里就不详细的说明JSON插件的安装了)。这地方我们可以JSOn提取器来获取相应数据中的access_token的值,并且定义一个变量token来存储它。具体就是选择login的接口后,右键在后置处理器中添加JSON提取器,具体如下:
在如上中,可以很清晰的看到定义了变量token,以及获取access_token的值的过程,当然还需要考虑到获取值失败的情况下默认返回的结果信息是Not Found Token,这是基于程序的容错思想来思考的。
JMeter中调用变量
下来在线程组中添加订单详细的接口,然后在请求头中调用变量token,在JMeter中调用变量的方式为${变量},下面具体显示的是订单详情中请求头中调用变量的信息,具体如下所示:
JMeter中验证结果信息
下面具体验证下这部分的结果信息,也就是执行,然后在查看结果树中查看接口返回的响应数据以及协议状态码,具体展示信息如下:
代码中关联解决思路
在函数式的编程方面还是面向对象的编程方面,如果单独的在一个函数的角度或者是一个方法的角度而言,其实函数或者说是方法有没返回值其实并不那么的重要,因为不管是函数还是方法,我们都需要实现的是它的表达式,也就是说我们输入,然后过程中经过函数以及方法的理解处理,然后输出结果,这个结果不管是return返回来函数print()的输出,其实都一样的。的确如此,但是函数需要与其他函数之间建立一层关联,这样函数就不是孤立的个体了,而是需要思考的点是怎么和其他的函数之间建立关系。而返回值可以很好的建立这种纽带的关系,通过这样的纽带关系,函数不再是一个孤立的个体,而是与其他的函数有了关系,个人觉得这是函数它的价值体现之一。当然对函数而言,一个函数可以有多个返回值,接收的时候就需要多个值来进行接收。
返回值对关联的解决
明白了函数的返回值后,再来看之前使用工具解决关联的思路,其实发现我们使用函数的返回值可以很轻松的来解决这个问题,那么定义的函数具体就是如下:
代码语言:javascript复制import requests
def login():
r=requests.post(
url='http://192.168.0.103:5000/auth',
json={"username":"wuya","password":"asd888"})
return r.json()['access_token']
来我们再来定义一个订单详细的接口信息,那么这样针对订单详情函数而言,就需要一个形式参数来接收登录的函数返回的值,因此也可以说函数的形式参数也可以是函数,具体定义的函数值如下:
代码语言:javascript复制import requests
import json
def login():
r=requests.post(
url='http://192.168.0.103:5000/auth',
json={"username":"wuya","password":"asd888"})
return r.json()['access_token']
def orderDetail(token):
r=requests.get(
url='http://192.168.0.103:5000/v1/order',
headers={'Authorization':'jwt {0}'.format(token)})
print(json.dumps(r.json(),ensure_ascii=False,indent=True))
if __name__ == '__main__':
orderDetail(token=login())
执行如上的代码后,就可以正确的返回响应结果信息。
Fixture的解决之道
在Pytest测试框架中,Fixture函数可以有两个维度的思考,具体可以总结为:
- 函数的返回值特性
- 测试框架的测试固件作用
那么这地方可以结合Fixture函数的返回值的特性来轻松的解决这个问题,但是前提是必须得结合Pytest测试框架来进行执行。下面把代码改造为Pytest测试框架可以执行的代码,改造后的代码具体为:
代码语言:javascript复制import requests
import json
import pytest
@pytest.fixture()
def login():
r=requests.post(
url='http://192.168.0.103:5000/auth',
json={"username":"wuya","password":"asd888"})
return r.json()['access_token']
def test_orderDetail(login):
r=requests.get(
url='http://192.168.0.103:5000/v1/order',
headers={'Authorization':'jwt {0}'.format(login)})
print(json.dumps(r.json(),ensure_ascii=False,indent=True))
if __name__ == '__main__':
pytest.main(["-v","-s","test_order.py::test_orderDetail"])
执行后,可以看到使用fixture的函数很轻松的解决了这部分的问题。感谢您的阅读,后续会持续更新!