实现接口的契约测试
在当前微服务和前后端分离大行其道的行业背景下,越来越多的团队采用了前后端分离和微服务的架构风格。 A团队开发某服务并提供对应API服务,B团队是A团队的使用者调用A团队的API。A团队埋头苦干,B团队也争分夺秒,两边都开发完了,往往一联调,就出现很多问题。
问题和困境 API调用方对API提供方的变更经常需要通过对API的测试来感知。 直接依赖真实API的测试效果受限与API提供方的稳定性和反应速度。
解决方案 解决方式首先是依赖关系的解耦,去掉直接对外部API的依赖,而是内部和外部系统都依赖于一个双方共同认可的约定—“契约”,并且约定内容的变化会被及时感知;其次,将系统之间的集成测试,转换为由契约生成的单元测试,例如通过契约描述的内容,构建测试替身。这样,同时契约替代外部API成为信息变更的载体
契约测试也叫消费者驱动测试。 两个角色:消费者(Consumer)和 生产者(Provider) 一个思想:需求驱动(消费者驱动) 契约文件:由Consumer端和Provider端共同定义的规范,包含API路径,输入,输出。通常由Consumber生成。 实现原理:Consumer 端提供一个类似“契约”的东西(如json 文件,约定好request和response)交给Provider 端,告诉Provider 有什么需求,然后Provider 根据这份“契约”去实现。
我们可以通过SCHEMA来实现接口的契约测试。
API测试:通过FAKER生成测试数据,通过SCHEMA检查返回结果
image
需求
假定有如主图相同的http请求。我们一般的做法是,用postman去抓取http请求,然后修改request的body或者header里的数据,点击send按钮,检查返回的response的body是否正确。
对于输入。一般来说,我们会纯手工,或者半自动的,设计测试用例。例如使用边界值分析,等价类划分等方法,用在我们的输入参数中。比如我参数中的configname最多200个参数,我测试输入201个参数。
对于输出。一般来说,我们大部分时候是肉眼检查,或者写代码,通过jsonpath取参数,然后判断是否存在来检查。
这里我打算用一个新的方法来降低测试的手工特性,让他更自动化一点。以下想法还处于调试阶段,用于大规模使用,暂时不行。
设计
输入修改方案:引入faker库和jsonschema库。通过这两个库,我们可以产生随机的json串
faker是我无意之间发现的,能按照规律产生随机字的库,例如
fake.name()
是产生一个随机的名字,只要加入适当的providers,就能按照需要的规则产生随机字
jsonschema这个用的人很多,这里就不介绍了,下面推荐一个网站,能把json请求转换为schema格式
https://www.liquid-technologies.com/online-json-to-schema-converter
schema中会注明每个字段的规则,例如是string类型还是integer。
输出修改方案:使用jsonschma的validate方法来检查(这种检查方法目前有一些检查不充分,但是已经可以让测试人员减少一些工作量了)
jsonschema.validate(response, schema)
使用方案
1.去postman抓取http请求,并且记录下所需要的输入json和输出json
2.打开https://jsonschema.net/ 把输入json和输入json 转换成jsonschema
3.把输入jsonschema文件,输出文件jsonschema放入相应的目录,自己写一个用于生成随机requestbody的provider和一个测试用的主函数
4.运行测试主入口文件,打印一下发送的json文件,看是不是随机化了,结果是确实随机化了。
代码
测试主入口test_json_from_schema.py
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import json
import faker
import jsonschema
import requests
from jsonschema.exceptions import ValidationError
import jsonprovider
def generate_request(request_json_schema):
'''
通过schema生成随机测试数据
:param request_json_schema:
:return:
'''
fake = faker.Faker()
fake.add_provider(jsonprovider.JSONProvider)
request_body = fake.json(json.load(open(request_json_schema)))
print(request_body)
return request_body
def check_json_schema(response, schema):
'''
通过json_schema检查返回的json串
:param response:
:param schema:
:return:
'''
result = True
try:
jsonschema.validate(response, schema)
except ValidationError, e:
print("fail")
result = False
return result
if __name__ == '__main__':
# 生成request body
body = generate_request("schema_file/create_config_request_schemas.json")
# 使用request库发送post请求
url = "https://dev.honcloud.honeywell.com.cn:8080/dashboard/clustercentre/configmng/newconfig/addconfig"
headers = {"Content-Type": "application/json", "authorization": "48a5eb61-914e-4b3a-a7a3-0b25f72d06d7"}
response = requests.post(url, data=body, headers=headers)
print(response.json())
response_json=response.json()
response_schema="schema_file/create_config_response_schemas.json"
# 用生成的response的schema来检查
result=check_json_schema(response_json,response_schema)
print(result)
其实定义好了契约,用mock来测试消费者,也是很方便的。 python的faker包,就能很好的实现Mock数据。
- Dora.Interception, 为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式
- Dora.Interception,为.NET Core度身打造的AOP框架:全新的版本
- ASP.NET Core的路由[4]:来认识一下实现路由的RouterMiddleware中间件
- 浅谈 Java 并发编程中的若干核心技术
- ASP.NET Core的路由[3]:Router的创建者——RouteBuilder
- ASP.NET Core的路由[2]:路由系统的核心对象——Router
- ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系
- 学习ASP.NET Core, 怎能不了解请求处理管道[6]: 管道是如何随着WebHost的开启被构建出来的?
- 学习ASP.NET Core, 怎能不了解请求处理管道[5]: 中间件注册可以除了可以使用Startup之外,还可以选择StartupFilter
- 学习ASP.NET Core, 怎能不了解请求处理管道[4]: 应用的入口——Startup
- 学习ASP.NET Core, 怎能不了解请求处理管道[3]: 自定义一个服务器感受一下管道是如何监听、接收和响应请求的
- .NET Core多平台开发体验[4]: Docker
- .NET Core多平台开发体验[3]: Linux (Windows Linux子系统)
- .NET Core多平台开发体验[2]: Mac OS X
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android 高仿微信朋友圈拍照上传功能
- android的ListView点击item使item展开的做法的实现代码
- Android NavigationView头部设置监听事件
- android如何取得本地通讯录的头像的原图的实现代码
- 取消Android Studio项目与SVN关联的方法
- Android编程实现获取当前系统语言及地区并更改语言的方法
- Android Studio绑定下拉框数据详解
- python中的socket实现ftp客户端和服务器收发文件及md5加密文件
- Android XRecyclerView实现多条目加载
- python3安装OCR识别库tesserocr过程图解
- Android studio 3.0上进行多渠道打包遇到的问题小结(超简洁版)
- Python自动重新加载模块详解(autoreload module)
- python自动脚本的pyautogui入门学习
- Android手机号码输入框(满11位自动跳到下个输入框)实例代码
- Android实现简单实用的搜索框