# API 测试
# HTTP 请求
对于测试模拟http请求,框架提供了THttpTemplate类,它包装了ApacheHttpClient以实现简单的http操作。
通过构造函数或者方法,可以设置请求的url、cookie、header等参数,需要注意的是,部分设置参数的方法会返回一个新对象。
简单示例:
import fly.test.web.client.THttpTemplate;
import fly.test.web.client.THttpResponse;
...
@Autowired
protected THttpTemplate http;
/**
* 设以下服务均引入了 Security
*/
@Test
public void testHttpTemplate() {
// 设未开启 ssl,端口号为8080,上下文为空,则根路径为 http://localhost:8080
THttpTemplate newHttp = http.authorization("Bearer accessToken");
http.get("/user").assertUnauthorized();
newHttp.get("/user").assert2xx();
THttpTemplate http1 = new THttpTemplate("http://localhost:8081");
http1.post("/user", New.multiValueMap("name", "A")).assertUnauthorized();
THttpTemplate.ClientBuilder clientBuilder = new THttpTemplate.ClientBuilder();
clientBuilder.rootUrl("http://localhost:8082")
.authorization("Bearer accessToken");
THttpTemplate http2 = new THttpTemplate(clientBuilder);
THttpResponse response2 = http2.post("/user", New.multiValueMap("name", "A"));
response2.assert2xx();
}
以上介绍了注入及通过两种构造方法使用THttpTemplate,通过注入的方法获取到的THttpTemplate,会根据外部条件改变参数,如:
- 判断是否开启了
ssl,若开启则为https,否则为http; - 根据当前参数设置端口号和上下文,假设未开启
ssl,端口号为8080,上下文为fly,则根路径为http://localhost:8080/fly。
对于THttpTemplate的返回THttpResponse,里面保存了请求响应的内容,以及包装了asserts方法:
import fly.test.web.client.THttpTemplate;
import fly.test.web.client.THttpResponse;
...
@Autowired
protected THttpTemplate http;
@Test
public void testHttpResponse() {
// user 列表查询
THttpResponse response = http.get("/user");
// 读取 body 并转换为 list,判断大小
String body = response.readBodyAsString();
List<Object> list = JSON.decode(body);
assertSize(list, 1);
// 检查请求头
Header total = response.getFirstHeader("X-Total-Count");
assertEquals("1", total.getValue());
// 检查状态
response.assertStatus(200);
}
# REST 模块
针对REST模块提供的功能测试,需要已有fly-rest依赖:
<dependency>
<groupId>cn.openfuse</groupId>
<artifactId>fly-rest</artifactId>
<version>${fly.version}</version>
</dependency>
# CRUD 配置
对应配置类:
fly.test.rest.crud.RestCrudTestProperties
application.yml:
fly:
test:
rest:
crud:
base-path: /api # 单元测试基础路径,默认为空
base-path
配置了基础路径后,使用TRestResource等进行单元测试,根路径则变为http://localhost:{port}/{context-path}/{base-path}。
# 客户端配置
对应配置类:
fly.test.rest.client.TRestProperties
application.yml:
fly:
test:
rest:
client:
username: test
password: 1
主要影响TRestTemplate等生成时是否使用basicAuth。
# REST 请求
相对于HTTP 请求中的THttpTemplate,TRestTemplate更适合RESTful风格的API测试。
建议使用注入的方式获取TRestTemplate对象,默认实现的bean应用到框架提供的配置,自动完成一些如baseAuth、ssl的处理。
示例,设存在以下RESTful API:
POST /userGET /user/{id}PATCH /user/{id}DELETE /user/{id}
@Autowired
protected TRestTemplate rest;
@Test
public void testRestTemplate() {
// 统一添加路径
TRestTemplate userRest = rest.nested("/user");
// POST /user,传入创建内容及指定返回类型
UserEntity user = userRest.postFor200(null, new UserEntity("name"), UserEntity.class);
// GET /user/{id}
// 方法中的 urlVariables 命名参数可以按顺序补充 url 中占位符如 {id}
UserEntity find = userRest.getFor200("/{id}", UserEntity.class, user.getId());
assertEquals(user.getName(), find.getName());
// PATCH /user/{id}
// Object 类型 request 命名参数可以传入不同类型的请求体内容
userRest.patchFor204("/{id}", New.hashMap("name", "newName"), user.getId());
find = userRest.getFor200("/{id}", UserEntity.class, user.getId());
assertEquals("newName", find.getName());
// DELETE /user/{id}
userRest.deleteFor204("/{id}", user.getId());
userRest.getFor404("/{id}", user.getId());
}
请求方法中的Object类型request命名参数,还可以传入如multipart或form类型的请求体,根据类型替换Content-Type请求头:
multipart(multipart/form-data)
@PostMapping("/multipart")
public void multipart(@RequestPart(name = "file") MultipartFile file) {
// TODO:
}
@Autowired
protected TRestTemplate rest;
@Test
public void testRestTemplate() {
// multipartEntity 方法三个参数分别为:
// 1. filename 文件名称
// 2. content 文件内容
// 3. parameter 接收参数名称
HttpEntity multipartEntity = rest.multipartEntity("test.txt", "fly", "file");
rest.postFor204("/multipart", multipartEntity);
}
创建的文件会作为临时文件保存。
form(application/x-www-form-urlencoded)
@PostMapping("/form")
public void multipart(@RequestParam Map<String, String> params) {
// TODO:
}
@Autowired
protected TRestTemplate rest;
@Test
public void testRestTemplate() {
HttpEntity formEntity = rest.formEntity(New.hashMap("name", "fly"));
rest.postFor204("/form", formEntity);
}
# CRUD 请求
TRestResource是由TRestTemplate再包装得来,通过TRestTemplate的resource方法创建。它增加了CRUD的概念,如从get/post改为find/query等方法调用,更符合数据访问中Dao的开发习惯。
@Autowired
protected TRestTemplate rest;
@Test
public void test() {
TRestResource userRest = rest.resource("/user");
// 创建一个用户,并测试返回结果是否包含对象id
UserEntity created = userRest.create(New.hashMap("name", "test"), UserEntity.class);
assertNotNull(created.getId());
// query 查询,根据id过滤,并检查数据是否匹配
userRest.query(UserEntity.class, TFilters.eq("id", created.getId())).assertFist()
.extracting(UserEntity::getId).isEqualTo(created.getId());
// 修改,find 查询检查需改是否成功
userRest.patchUpdate(created.getId(), New.hashMap("name", "test1"));
UserEntity find = userRest.find(UserEntity.class, created.getId());
assertEquals("test1", find.getName());
// 删除,再次 find 查询,期望结果接口返回 NotFound 异常
userRest.delete(find.getId());
userRest.findForNotFound(find.getId());
}