背景

最开始接触公司的接口自动化项目,发现里面几乎都是原生RestAssured的API调用.
RestAssured绝对是很强大的,但是很多业务测试的同学只会一些基础的Java,就导致编写的用例很多重复性的代码.
所以我就萌生了造轮子的想法,欢迎大佬们提供意见建议~

介绍

基本的思路还是针对 RestAssured 的封装,最好的效果是能够让人感觉不到 RestAssured 的存在,而让测试同学只关心接口和场景的组织以及对应校验逻辑.

所以结合 JAVA 语言面向对象的特性,将接口抽象为一个 Java 对象,通过在对象上添加对应注解来配置接口相关属性,例如 GET 参数,ContentType,Payload 等等.

PS: 将接口抽象为对象的形式,我是参考了某大厂的框架思想,但没有使用他任何一行代码.如果有大神知道这种思路最原始的框架欢迎告知下~

详细介绍

Test-Assured

我们的目标: 以更加优雅和简洁的方式设计自动化接口与自动化场景

技术选型

使用文档

0.准备阶段


<dependency>
  <groupId>io.github.diduweiwu</groupId>
  <artifactId>test-assured</artifactId>
  <version>1.1.1-REALEASE</version>
</dependency>

1.开发阶段

待依赖拉取完成后,即可开启我们的接口开发之旅
针对接口自动化调用,目前有两种场景

1.1 单接口用例

注: 单接口用例是场景化用例的基础


使用

1.1.1 新建实体类

一个实体类对应一个接口,此处假设这是个查询用户信息的接口

class UserInfo {

}
1.1.2 添加注解

@Post
@Host("http://0.0.0.0:9001")
@ContentType("application/json")
class UserInfo implements Api{

}

解释

1.1.3 接口调用

当定义好接口对象后,就可以进行调用了

初始化接口对象实例,作为入参使用 RequestUtil 的一系列 send 方法进行调用:

1.2 多接口调用

当单接口调试通过后,某些情况下需要将一些接口组合成场景用例,比如某个业务域对象的CRUD流程

使用

1.2.1 初始化场景

使用 RequestScene 静态函数相关方法构建场景

//将第一个接口调用的response返回值作为入参来初始化场景
RequestScene.of(RequestUtil.send(new UserInfo()));

或者,如果 UserInfo 对象实现了 Api 接口,则可以直接传入对象

//将第一个接口调用的response返回值作为入参来初始化场景
RequestScene.of(new UserInfo());
1.2.2 链式串联接口调用

理论上大部分接口都是链式调用,故暂未考虑并行接口调用的场景

调用 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()));
1.2.3 触发场景接口调用

链式接口调用串联完成后,调用.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();

2.扩展阶段

作为一个框架,支持 OPEN-CLOSE 原则是最基本的,接下来是扩展自定义注解的方法

2.1 自定义注解

新建一个功能注解,定义好该注解的相关类型,然后对其添加 IAnnotation 注解,仅标注了该注解才会被识别


@Inherited
@IAnnotation
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface My {
    String value();
}

如上所示,由于请求信息配置大部分是 key-value 或者单独 value 的形式,所以按需配置 key 或者 value 即可
这里定义了一个 My 注解,该注解有 value 一个字段并且必填

2.2 自定义处理器

新建一个功能处理类,该类需要实现 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 的值

2.3 关联注解与注解处理器

最终需要在 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;
}

2.4 使用注解

此时可在定义的接口对象上使用自定义注解

@My(value = "abandon")
private String search;

执行接口调用之前在请求中设置一个 cookie,键值对为 My->abandon

至此自定义注解扩充流程完成

可用注解,注解可以添加在属性上,也可以添加在方法上,通过返回值来动态赋值

写在最后

由于这是比较初始的版本,肯定有很多考虑步骤和可优化的地方,欢迎大家使用、反馈和参与扩充功能:)

TODO


附上 github 地址: https://github.com/diduweiwu/test-assured


↙↙↙阅读原文可查看相关链接,并与作者交流