一、背景:
想根据两个不同的注解筛选case
@CaseTag 包含key 与val
@CaseGroup 包含 group 与team
想要跟据@CaseTag @CaseGroup 这两个组合筛选case,既可以通过@CaseTag 筛选,
也可以根据@CaseGroup 筛选。
二、编写注解@CaseGroup
CaseGroup
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 CaseGroup {
String team();
String group();
}
三、扩展 筛选注解 @CaseSelector
筛选case时,有@CaseSelector负责筛选入口
执行筛选case入口:
@CaseSelector(scanPackage = "com.example.autoapi.cases",key="level",val="redLine")
public void redLine(){
}
@CaseSelector(scanPackage = "com.example.autoapi.cases.login",team="qa",group="normal")
public void teamTest(){
}
CaseSelector 需要有@CaseTag属性,也需要有@CaseGroup属性。
扩展一下CaseSelector
CaseSelector
package com.example.autoapi.annotation;
import com.example.autoapi.extension.CaseAnnotationCheckExtension;
import com.example.autoapi.extension.CaseSelectorExtension;
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(CaseSelectorExtension.class) // 与junit结合,由junit提供。自定义一个扩展类,然后放到这里。走我们的自定义类
public @interface CaseSelector {
String scanPackage();
String key() default "";
String val() default "";
String team() default "";
String group() default "";
}
三、负责筛选的自定义扩展类CaseSelectorExtension
CaseSelectorExtension
package com.example.autoapi.extension;
import com.example.autoapi.annotation.CaseSelector;
import com.example.autoapi.extension.filter.CaseGroupFilter;
import com.example.autoapi.extension.filter.CaseTagFilter;
import com.example.autoapi.util.RequireUtil;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.engine.DiscoverySelector;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import java.lang.reflect.Method;
public class CaseSelectorExtension implements BeforeTestExecutionCallback{
@Override
public void beforeTestExecution(ExtensionContext extensionContext) throws Exception {
Method method = extensionContext.getRequiredTestMethod();
// 获取注解CaseSelector信息
CaseSelector caseSelector = method.getAnnotation(CaseSelector.class);
// 验证/校验 注解信息
verify(caseSelector);
//----开始进行是筛选----
// 先筛选包
// 再按照@CaseTag key value筛选
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
// 根据包筛选case
.selectors(DiscoverySelectors.selectPackage(caseSelector.scanPackage()))
// 根据注解CaseTag筛选case
.filters(new CaseTagFilter(caseSelector))
.filters(new CaseGroupFilter(caseSelector))
.build();
Launcher launcher = LauncherFactory.create();
launcher.execute(request);
// ----以上固定结构,框架提供能力
// 根据CaseTag 筛选,这里是需要自己写的CaseTagFilter
}
private void verify(CaseSelector caseSelector){
RequireUtil.requireNotNullOrEmpty(caseSelector.scanPackage(),"scanPackage is must");
}
}
注意:
这里分别负责注解@CaseTag的筛选
注解@CaseGroup的筛选。
这里用了一个类似于责任链似的设计模式。
我想组合筛选@CaseTag 与 @CaseGroup。
流程:
先筛选@CaseTag,在筛选的结果后再进行@CaseGroup 筛选。
因为参与筛选的注解,不是都是必填的,如果有的注解没有填写,那么就不对这个注解进行筛选。
模版抽象类
AbstractDiscoveryFilter
package com.example.autoapi.extension.filter;
import com.example.autoapi.annotation.CaseSelector;
import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
import org.junit.platform.engine.FilterResult;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.launcher.PostDiscoveryFilter;
public abstract class AbstractDiscoveryFilter implements PostDiscoveryFilter {
// preFilter 判断是否执行当前过滤器,为true执行,为false不执行
// onApply,具体的执行过滤,preFilter 为true时,执行onApply
protected CaseSelector caseSelector;
public AbstractDiscoveryFilter(CaseSelector caseSelector) {
this.caseSelector = caseSelector;
}
protected abstract boolean preFilter(CaseSelector caseSelector);
// 类似于责任链的设计模式。是否需要当前过滤器执行
// 为true,需要执行当前过滤器,则调用下方的onApply方法,去实行过滤
// 为false,不需要执行当前过滤器。交给下一个过滤器去执行
// 这个方法是抽象的,交给子类去实现
protected abstract FilterResult onApply(TestMethodTestDescriptor testDescriptor);
@Override
public FilterResult apply(TestDescriptor testDescriptor) {
if(!preFilter(caseSelector)){ // 如果不需要过滤
return FilterResult.includedIf(true); // 返回true,放过所有的case。
}
if(testDescriptor instanceof TestMethodTestDescriptor){
return onApply((TestMethodTestDescriptor)testDescriptor);
}
return FilterResult.includedIf(false);// false 全部拦住,正常情况下不会出现这种情况
}
}
CaseTagFilter
CaseTagFilter实现那个模版AbstractDiscoveryFilter
package com.example.autoapi.extension.filter;
import com.example.autoapi.annotation.CaseSelector;
import com.example.autoapi.annotation.CaseTag;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
import org.junit.platform.engine.FilterResult;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.launcher.PostDiscoveryFilter;
import java.lang.reflect.Method;
import java.util.Arrays;
public class CaseTagFilter extends AbstractDiscoveryFilter{
public CaseTagFilter(CaseSelector caseSelector) {
super(caseSelector);
}
@Override
protected boolean preFilter(CaseSelector caseSelector) {
return StringUtils.isNotBlank(caseSelector.key())&& StringUtils.isNotBlank(caseSelector.val());
}
@Override
protected FilterResult onApply(TestMethodTestDescriptor testDescriptor) {
Method testMethod = testDescriptor.getTestMethod();
CaseTag[] caseTags = testMethod.getAnnotationsByType(CaseTag.class);
long count = Arrays.stream(caseTags)
.filter(caseTag -> caseTag.key().equals(caseSelector.key()) && caseTag.val().equals(caseSelector.val()))
.count();
return count>0 ? FilterResult.includedIf(true):FilterResult.includedIf(false);
}
}
CaseGroupFilter
CaseGroupFilter实现那个模版AbstractDiscoveryFilter
package com.example.autoapi.extension.filter;
import com.example.autoapi.annotation.CaseGroup;
import com.example.autoapi.annotation.CaseSelector;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
import org.junit.platform.engine.FilterResult;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.launcher.PostDiscoveryFilter;
import java.lang.reflect.Method;
public class CaseGroupFilter extends AbstractDiscoveryFilter {
public CaseGroupFilter(CaseSelector caseSelector) {
super(caseSelector);
}
@Override
protected boolean preFilter(CaseSelector caseSelector) {
return StringUtils.isNotBlank(caseSelector.group())&&StringUtils.isNotBlank(caseSelector.team());
// 判断注解CaseSelector 的group与team是否为空,若都不为空 ,启用我们这个筛选器
}
@Override
protected FilterResult onApply(TestMethodTestDescriptor testDescriptor) {
Method testMethod = testDescriptor.getTestMethod();
// 该注解是非必填的,所以这里判断一下注解是否存在。
// 其实我个人觉得这里没必要判断是否存在,因为用的这个模式,在场景里,需要调用到这个方法的时候,该注解肯定是存的
boolean caseGroupSet = testMethod.isAnnotationPresent(CaseGroup.class);
if(caseGroupSet){
CaseGroup caseGroup = testMethod.getAnnotation(CaseGroup.class);
if(caseGroup.group().equals(caseSelector.group())&&caseGroup.team().equals(caseSelector.team())){
return FilterResult.includedIf(true);
}
}
return FilterResult.includedIf(false);
}
}
--------------------------补充一下这个设计的思路---------------------
使用的模式