最开始接触公司的接口自动化项目,发现里面几乎都是原生RestAssured的API调用.
RestAssured绝对是很强大的,但是很多业务测试的同学只会一些基础的Java,就导致编写的用例很多重复性的代码.
所以我就萌生了造轮子的想法,欢迎大佬们提供意见建议~
基本的思路还是针对 RestAssured 的封装,最好的效果是能够让人感觉不到 RestAssured 的存在,而让测试同学只关心接口和场景的组织以及对应校验逻辑.
所以结合 JAVA 语言面向对象的特性,将接口抽象为一个 Java 对象,通过在对象上添加对应注解来配置接口相关属性,例如 GET 参数,ContentType,Payload 等等.
PS: 将接口抽象为对象的形式,我是参考了某大厂的框架思想,但没有使用他任何一行代码.如果有大神知道这种思路最原始的框架欢迎告知下~
我们的目标: 以更加优雅和简洁的方式设计自动化接口与自动化场景
<dependency>
<groupId>io.github.diduweiwu</groupId>
<artifactId>test-assured</artifactId>
<version>1.1.1-REALEASE</version>
</dependency>
待依赖拉取完成后,即可开启我们的接口开发之旅
针对接口自动化调用,目前有两种场景
注: 单接口用例是场景化用例的基础
一个实体类对应一个接口,此处假设这是个查询用户信息的接口
class UserInfo {
}
@Post
@Host("http://0.0.0.0:9001")
@ContentType("application/json")
class UserInfo implements Api{
}
解释
当定义好接口对象后,就可以进行调用了
初始化接口对象实例,作为入参使用 RequestUtil 的一系列 send 方法进行调用:
send(new UserInfo(), setUps, postChecks)
// 建议使用lambda表达式传入,req是rest-assured的原生Request对象
List<ISetUp> setUps = ListUtil.of(req -> req.header("FITURE", "Yes"));
// 建议使用lambda表达式传入,res是rest-assured的原生response对象
List<IPostCheck> postChecks=ListUtil.of(
res->Assert.notContain(res.body().asString(),"name","返回值不能包含关键字")
);
还有其他一些方法参考 RequestUtil 静态类,详细可参考 RequestUtil 静态类,常用方法都添加了注释说明
当单接口调试通过后,某些情况下需要将一些接口组合成场景用例,比如某个业务域对象的CRUD流程
使用 RequestScene 静态函数相关方法构建场景
//将第一个接口调用的response返回值作为入参来初始化场景
RequestScene.of(RequestUtil.send(new UserInfo()));
或者,如果 UserInfo 对象实现了 Api 接口,则可以直接传入对象
//将第一个接口调用的response返回值作为入参来初始化场景
RequestScene.of(new UserInfo());
理论上大部分接口都是链式调用,故暂未考虑并行接口调用的场景
调用 then() 方法并传入 1+ 个接口调用作为入参
then() 方法也支持 Function类型的 lambda 表达式作为入参,此时会将上次接口调用的 response 作为入参传入,如此可从上个接口取值进行使用
then() 方法也支持传入实现了 Api 接口的请求对象传入
RequestScene.of(RequestUtil.send(new User()))
.then(RequestUtil.send(new User()))
.then(
RequestUtil.send(new User()),
RequestUtil.send(new User())
)
.then(new User())
.then(r->RequestUtil.send(new User()));
链式接口调用串联完成后,调用.complete() 方法即可触发整个场景接口的调用
complete() 方法返回的是最后一个接口的响应
RequestScene.of(RequestUtil.send(new User()))
.then(RequestUtil.send(new User()))
.then(
RequestUtil.send(new User()),
RequestUtil.send(new User())
).then(r->RequestUtil.send(new User()))
.complete();
作为一个框架,支持 OPEN-CLOSE 原则是最基本的,接下来是扩展自定义注解的方法
新建一个功能注解,定义好该注解的相关类型,然后对其添加 IAnnotation 注解,仅标注了该注解才会被识别
@Inherited
@IAnnotation
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface My {
String value();
}
如上所示,由于请求信息配置大部分是 key-value 或者单独 value 的形式,所以按需配置 key 或者 value 即可
这里定义了一个 My 注解,该注解有 value 一个字段并且必填
新建一个功能处理类,该类需要实现 IProcessor 接口,并重写 execute 方法
public class MyProcessor implements IProcessor {
@Override
public void execute(Object api, AnnotatedElement element, Class<? extends Annotation> clazzAnnotation, RequestSpecification request) {
this.executeSingValue(api, element, clazzAnnotation, value -> request.cookie("My", value));
}
}
如上所示,我们需要在 execute 里面实现自定义的请求处理逻辑
由于自定义注解 My 只有一个 value 参数,故调用上层接口的 executeSingValue 方法并传入对应注解和逻辑处理代码
executeSingValue 方法需要注意第四个参数为 value 获取后的 lambda 表达式处理逻辑
此处我们将 My 注解上配置的 value 设置为 cookie 里面的键为 My 的值
最终需要在 My 注解中设置 processor 属性为 MyProcessor 的 Clazz 类型
@Inherited
@IAnnotation
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface My {
String value();
Class<? extends IProcessor> processor() default MyProcessor.class;
}
此时可在定义的接口对象上使用自定义注解
@My(value = "abandon")
private String search;
执行接口调用之前在请求中设置一个 cookie,键值对为 My->abandon
至此自定义注解扩充流程完成
可用注解,注解可以添加在属性上,也可以添加在方法上,通过返回值来动态赋值
请求动词
请求参数
框架基础
请求头信息
Cookie 信息
其他请求配置
BearToken 配置
由于这是比较初始的版本,肯定有很多考虑步骤和可优化的地方,欢迎大家使用、反馈和参与扩充功能:)
附上 github 地址: https://github.com/diduweiwu/test-assured