junit5的自带注解,不能满足需要,所以我们自定义一些注解

自定义注解:
1、自定义注解,@CaseTitle 用例标题 @CaseTag @CaseTags 用例标签,可以打多个,所以有数组 @CheckPoint @CheckPoints 用例检查点,可以是多个。
一、Java自定义注解:
举例一:@CaseDesc
1、新建注解 new Java Class -》Annotation

package com.example.autoapi.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD}) // Target表示将来这个注解应用在哪些方面。注解可以应用在类上、方法上(此处只应用在方法上)
@Retention(RetentionPolicy.RUNTIME) //运行时,使用这个这个注解
public @interface CaseDesc {
String desc();
String owner();
}
注解编写:
1、运行时,使用这个注解:
@Retention(RetentionPolicy.RUNTIME)
2、该注解作用域,可以作用在类/方法上。此处我们只作用在方法上
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD})
3、注解的具体内容,我们编写的是@CaseDesc注解,里面包含描述信息 和维护者
String desc();
String owner();
举例二:@CaseTag @CaseTags
@CaseTag,用例的tag,与@CaseDesc的区别是,用例可以写多个@CaseTag 。
@CaseTag
package com.example.autoapi.annotation;
import java.lang.annotation.*;
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD}) // Target表示将来这个注解应用在哪些方面。注解可以应用在类上、方法上(此处只应用在方法上)
@Retention(RetentionPolicy.RUNTIME) //运行时,使用这个这个注解
@Repeatable(CaseTags.class) // 支持多个CaseTag
public @interface CaseTag {
String key();
String value();
}
@CaseTags
package com.example.autoapi.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD}) // Target表示将来这个注解应用在哪些方面。注解可以应用在类上、方法上(此处只应用在方法上)
@Retention(RetentionPolicy.RUNTIME) //运行时,使用这个这个注解
public @interface CaseTags {
CaseTag[] value();
}
@CaseTags的内容,是@CaseTag的数组
在@CaseTag上使用@Repeatable(CaseTags.class) ,将两者关联起来。
这样就可以在方法上写多个@CaseTag注解了。
完成以上步骤(以上只是单纯的使用Java的能力写注解),就可以使用这些注解了。可以在case上使用这些注解,但是这些注解没有具体的功能。
想要有具体的功能,我们要将这些自定义注解结合junit5框架。
二、我们要实现哪些功能?
1、注解自身格式的检查(注解是否为空,注解内容是否填写正确)
2、根据注解不同来筛选运行case
3、在case上打报警标签
等
三、自定义注解与junit5框架相结合--注解自身格式的检查
思路:
1、自定义注解@AutoTest,作为一个载体,来收集使用@AutoTest 注解的测试case上所有的注解
- 测试方法/case使用@AutoTest注解
- 拿到这个测试方法的名字
- 利用反射原理,拿到这个测试方法上的所有注解
- 然后对所有注解进行处理
@AutoTest
package com.example.autoapi.annotation;
import com.example.autoapi.extension.CaseAnnotationCheckExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD}) // Target表示将来这个注解应用在哪些方面。注解可以应用在类上、方法上(此处只应用在方法上)
@Retention(RetentionPolicy.RUNTIME) //运行时,使用这个这个注解
@Test
@ExtendWith(CaseAnnotationCheckExtension.class) // 与junit结合,由junit提供。自定义一个扩展类,然后放到这里。走我们的自定义类
public @interface AutoTest {
}
1、要写@Test,这个是junit5运行case用的
2、@ExtendWith(CaseAnnotationCheckExtension.class)
与junit结合,由junit提供。自定义一个扩展类,然后放到这里。走我们的自定义类。
在这个自定义类里,我们收集所有的注解,然后进行处理
CaseAnnotationCheckExtension
package com.example.autoapi.extension;
import com.example.autoapi.annotation.CaseTitle;
import com.example.autoapi.check.ObserverManager;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import java.lang.reflect.Method;
// 自定义的扩展类,必须实现框架提供的BeforeTestExecutionCallback接口
// BeforeTestExecutionCallback 在执行case之前,先执行我们的回调
public class CaseAnnotationCheckExtension implements BeforeTestExecutionCallback {
@Override
public void beforeTestExecution(ExtensionContext extensionContext) throws Exception {
Method requiredTestMethod = extensionContext.getRequiredTestMethod(); //拿到使用该注解的方法/测试case
ObserverManager.of().check(requiredTestMethod); // 利用观察者模式对case上的方法依次进行检查
}
}
自定义扩展类:CaseAnnotationCheckExtension
要想在junit5框架里,使用这个自定义扩展类,必须实现接口BeforeTestExecutionCallback
BeforeTestExecutionCallback 在执行case之前,先执行我们的回调。
ObserverManager.of().check(requiredTestMethod); // 利用观察者模式对case上的方法依次进行检查

这里我们使用了观察者设计模式,因为要检查很多个注解。
观察者设计模式:
1、我们要写很多个检查方法,比如检查@CaseTitle,注解是否必填,格式是否正确。
检查@CaseTag,注解是否必填,格式是否正确等等,我们要针对每一个注解写一个检查方法。
2、定义观察者接口,里面有检查方法
AnnotationCheckObserver
package com.example.autoapi.check;
import java.lang.reflect.Method;
// 观察者模式
// 每个注解的检查都实现这个接口,便于统一管理/校验注解
public interface AnnotationCheckObserver {
void check(Method method);
}
3、每个注解的检查都实现这个接口,便于统一管理/校验注解
CaseTitleCheck
package com.example.autoapi.check;
import com.example.autoapi.annotation.CaseTitle;
import com.example.autoapi.exception.IllegaFormatException;
import com.example.autoapi.util.RequireUtil;
import java.lang.reflect.Method;
public class CaseTitleCheck implements AnnotationCheckObserver{
@Override
public void check(Method method){
boolean titleSet = method.isAnnotationPresent(CaseTitle.class);
// 判断注解CaseTitle是否存在
if(!titleSet){
throw new IllegaFormatException("title is must");
}
CaseTitle caseTitle = method.getAnnotation(CaseTitle.class);
RequireUtil.requireNotNullOrEmpty(caseTitle.value(),"CaseTitle value is must.eg: @CaseTitle(\"标题\")");
}
}
里面,写的是对应注解的检查方法
4、ObserverManager。管理所有的检查方法
package com.example.autoapi.check;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ObserverManager {
private final List<AnnotationCheckObserver> observers;
// 构造器,私有,不能被new
private ObserverManager(){
observers = new ArrayList<>();
observers.add(new CaseTitleCheck());// 添加,检查标题的方法
}
// ClassHolder属于静态内部类,在加载类Demo03的时候,只会加载内部类ClassHolder,
// 但是不会把内部类的属性加载出来
private static class ClassHolder{
// 这里执行类加载,是jvm来执行类加载,它一定是单例的,不存在线程安全问题
// 这里不是调用,是类加载,是成员变量
private static final ObserverManager holder =new ObserverManager();
}
public static ObserverManager of(){//第一次调用getInstance()的时候赋值
return ClassHolder.holder;
}
public void check(Method method){
// 把case/方法 method ,依次接受各种检查
for(AnnotationCheckObserver observer:observers){
observer.check(method);
}
}
}
1、每个注解的检查类都实现了AnnotationCheckObserver 这个接口,实现了 check方法
每个类都属于AnnotationCheckObserver接口,所以 创建一个集合,把所有的检查类都放在里面。
然后便利集合里面的元素,依次调用检查check方法。
----------------------------以上就是整个流程--------------------------
接下来完成,其他注解的检查
CaseDescCheck
package com.example.autoapi.check;
import com.example.autoapi.annotation.CaseDesc;
import com.example.autoapi.annotation.CaseTitle;
import com.example.autoapi.exception.IllegaFormatException;
import com.example.autoapi.util.RequireUtil;
import java.lang.reflect.Method;
public class CaseDescCheck implements AnnotationCheckObserver{
@Override
public void check(Method method){
boolean caseDescSet = method.isAnnotationPresent(CaseDesc.class);
// 判断注解CaseTitle是否存在
if(!caseDescSet){
throw new IllegaFormatException("CaseDesc is must");
}
CaseDesc caseDesc = method.getAnnotation(CaseDesc.class);
RequireUtil.requireNotNullOrEmpty(caseDesc.desc(),"CaseDesc desc is must.eg: @CaseDesc(desc=\"描述\",owner=\"管理者\")");
RequireUtil.requireNotNullOrEmpty(caseDesc.owner(),"CaseDesc owner is must.eg: @CaseDesc(desc=\"描述\",owner=\"管理者\")");
}
}
CaseTagCheck
package com.example.autoapi.check;
import com.example.autoapi.annotation.CaseDesc;
import com.example.autoapi.annotation.CaseTag;
import com.example.autoapi.exception.IllegaFormatException;
import com.example.autoapi.util.RequireUtil;
import java.lang.reflect.Method;
public class CaseTagCheck implements AnnotationCheckObserver{
@Override
public void check(Method method){
CaseTag[] caseTags = method.getAnnotationsByType(CaseTag.class);
// 判断是否有CaseTag注解
// 因为可以写多个,这里获取的是一个数组
// 数组长度为0时,报异常
if(caseTags.length==0){
throw new IllegaFormatException("CaseTag is must");
}
for(CaseTag caseTag:caseTags){
RequireUtil.requireNotNullOrEmpty(caseTag.key(),"CaseTag key is must.eg: @CaseTag(key=\"key\",val=\"val\")");
RequireUtil.requireNotNullOrEmpty(caseTag.val(),"CaseTag val is must.eg: @CaseTag(key=\"key\",val=\"val\")");
}
}
}
CheckPointCheck
package com.example.autoapi.check;
import com.example.autoapi.annotation.CaseTag;
import com.example.autoapi.annotation.CheckPoint;
import com.example.autoapi.exception.IllegaFormatException;
import com.example.autoapi.util.RequireUtil;
import java.lang.reflect.Method;
public class CheckPointCheck implements AnnotationCheckObserver{
@Override
public void check(Method method){
CheckPoint[] checkPoints = method.getAnnotationsByType(CheckPoint.class);
// 判断是否有CaseTag注解
// 因为可以写多个,这里获取的是一个数组
// 数组长度为0时,报异常
if(checkPoints.length==0){
throw new IllegaFormatException("CheckPoint is must");
}
for(CheckPoint checkPoint:checkPoints){
RequireUtil.requireNotNullOrEmpty(checkPoint.value(),"CheckPoint is must.eg: @CheckPoint(xxx)");
}
}
}
ObserverManager
package com.example.autoapi.check;
import com.google.common.collect.Lists;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ObserverManager {
private final List<AnnotationCheckObserver> observers;
// 构造器,私有,不能被new
private ObserverManager(){
observers = Lists.newArrayList(
new CaseTitleCheck(),
new CaseDescCheck(),
new CaseTagCheck(),
new CheckPointCheck()
);
}
// ClassHolder属于静态内部类,在加载类Demo03的时候,只会加载内部类ClassHolder,
// 但是不会把内部类的属性加载出来
private static class ClassHolder{
// 这里执行类加载,是jvm来执行类加载,它一定是单例的,不存在线程安全问题
// 这里不是调用,是类加载,是成员变量
private static final ObserverManager holder =new ObserverManager();
}
public static ObserverManager of(){//第一次调用getInstance()的时候赋值
return ClassHolder.holder;
}
public void check(Method method){
// 把case/方法 method ,依次接受各种检查
for(AnnotationCheckObserver observer:observers){
observer.check(method);
}
}
}
------------------------------补充说明---观察者模式----------------------------------
观察者模式
1、创建观察者接口,具体的每个检查类,去实现它,就是为了将来好组建成一个集合,把每个检查类放进去。
2、然后再遍历每个元素的check方法
