在实际工作中,经常会面临针对单个微服务的测试或者是OpenApi中提供了某一个API进行测试。那么针对这部分只需要测试研发被提供的接口发送请求后返回协议状态码是200就可以了吗?很显然这种简单的测试模式一方面针对服务测试的覆盖率不全,第二是输出的测试报告也是很难让别人信服的。下面详细地阐述这部分的测试策略以及测试过程中需要考虑的各个点和测试范围。
服务交互
不管是测试单个服务还是某个服务提供了API需要测试,它的思想本质是不会改变的,那么这个本质是什么呢?其实就是客户端与服务端之间的交互,不管是单体架构模式还是微服务架构模式而言,服务提供的API本质上都是客户端与服务端之间的交互,只不过在通信模式有区别而已。大多数我们见到的都是同步通信的模式,也就是客户端向服务端发送请求后,服务端需要响应回复客户端的请求,具体交互过程如下所示。
如上的交互模式一般都叫请求/响应模式,当然也可以说同步通信模式。
有了同步通信必然会有异步通信的模式,那么为什么会有异步通信的模式呢?这主要是因为同步通信虽然能够满足大部分的场景,但是在某些场景下使用异步通信模式更加具备优势,同时同步通信模式还存在请求请求超时的问题,具体来说就是客户端发送请求后当服务端存在异常导致客户端的请求一致超时,第二是同步通信由于存在超时问题可能导致任务堵塞。毕竟在工作中实际测试的产品服务与服务之间的交互虽然没有不是高并发的情况但是客户端的请求不可能是只发送一个请求,而是在产品业务链交互过程中客户端与服务端会不断的发生交互。因此由于同步通信模式存在缺陷,也就有了异步通信的模式,它的优势是客户端与服务端交互的过程中并不需要关注对方的存在,更多关注的是消息。对客户端而言,它不需要关注服务端是否存在,它关注的是消息发送后把消息经过处理后的结果返回给客户端就可以了,异步通信模式具体如下。
单接口测试维度
服务最终提供的都是API进行测试,因此首先需要的是针对单个接口的测试策略与思路,总结下来具体如下几点:
- 验证请求参数中必填参数是否做了为空校验
- 验证请求参数的数据类型是否做了判断
- 验证请求参数的字段长度是否做了长度限制的判断
- 验证请求参数是否做了填写特定值的判断
下面结合具体的案例来说下这部分,首先来看被测服务的源代码,具体如下:
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# author:无涯
from flask import Flask,jsonify
from flask_restful import Api,Resource,reqparse
app=Flask(__name__)
api=Api(app)
class LoginView(Resource):
def get(self):
return {'status':0,'msg':'ok','data':'this is a login page'}
def post(self):
parser=reqparse.RequestParser()
parser.add_argument('username', type=str, required=True, help='用户名不能为空')
parser.add_argument('password',type=str,required=True,help='账户密码不能为空')
parser.add_argument('age',type=int,help='年龄必须为正整数')
parser.add_argument('sex',type=str,help='性别只能是男或者女',choices=['女','男'])
args=parser.parse_args()
return jsonify(args)
api.add_resource(LoginView,'/login',endpoint='login')
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0')
在如上代码中可以看到服务端针对为空以及请求参数数据类型都做了处理,比如年龄只能是正整数而不能是字符串,性能只能是指定的特定值也就是男或者是女而不能是其他。来一个简单的案例实战,如年龄传的是字符串,看服务端服务的错误信息,具体如下:
如上所示服务端针对年龄不规范的请求参数在后端做了处理。另外一点需要考虑的是请求参数中字段的长度限制,如POST请求是往数据库中添加一条数据,如请求参数里面包含了书籍名称等字段,数据库设计书籍名称字段是10,但是请求参数中长度超过了10,如果后端针对这部分没做处理的时候导致在数据库这层出错,进而导致服务端这层也就出了问题。所以如上总结的针对单接口的测试维度每个点的背后都是存在它的逻辑合理性与测试范围完整性的考虑。
业务驱动
针对服务提供的接口仅仅考虑单接口的测试维度是不够的,也要结合产品的业务来验证服务提供的API合理性和业务完整性。服务提供API的最终目的是服务业务,因此满足业务特性是第一要素。如服务请求参数是username与password,返回的TOKEN作为其他服务访问系统的认证凭证,但是返回的TOKEN又是加密的。在这样的一个业务形态中,仅仅验证接口是否返回TOKEN是不够的,也要验证返回的TOKEN它的有效性、和它的时效性,总结下来就是:
- 返回了TOKEN不代表TOKEN可用,因此把它作为其他服务请求参数验证它的可用性
- 返回的TOKEN可用也要考虑它的时效性,如返回的TOKEN5分钟过期,那么很明显这是不合理的。具体它的时效性是多少合适了,还是需要结合具体的业务和整体的产品和系统架构来规定这个时间
服务稳定性
先不谈作为服务测试为什么需要考虑服务的稳定性。首先来说在架构设计的层面需要考虑服务的高可用,所谓服务的高可用指的是系统应该最大限度的保证服务可用性,缩短服务因为各种故障而不可用的时间,让服务具备很好的稳定性与易错性。在架构层面考虑的高可用具体指的就是服务的容错能力、故障转移和系统监控。服务稳定性这部分可以使用混沌工程的模式来进行测试和验证,在后续文章中专门阐述服务稳定性这部分的测试策略与对应的保障方式。
作为服务还需要考虑客户端在高并发的请求下服务端处理任务的能力,那么需要验证的点就是在多少并发请求下服务能够持续的提供能力而不致于服务出现瘫痪,同时响应时间在客户端的接收范围内。一般需要关注的是如最大响应时间、最小响应时间、中位数以及百分之99的请求响应时间。
服务安全性
服务的安全性在测试的过程中是必然需要考虑的,如果服务缺少验证,那么会存在很大的隐患,如任何人都可以调用服务的API获取数据以及使用如爬虫的技术进行高并发的请求导致服务资源出现瓶颈最终导致服务出现负载而瘫痪。
最后思考
测试单个服务不要局限于服务发送请求后返回200就认为已经测试到位,而是要从服务的功能性、业务性、稳定性、安全性等维度来对服务进行测试,从而保障服务的稳定并且可持续提供产品的能力。