上一节介绍了基于 junit4 实现 junitperf,但是可以发现定义变量的方式依然不够优雅。
那可以让用户使用起来更加自然一些吗?
有的,junit5 为我们带来了更加强大的功能。
拓展阅读:
浅谈性能测试
基于 junit4 分析 junitperf 源码,junit4 99% 的人都不知道的秘密!
我们首先回顾一下 junit4 的写法:
public class HelloWorldTest { @Rule public JunitPerfRule junitPerfRule = new JunitPerfRule(); /** * 单一线程,执行 1000ms,默认以 html 输出测试结果 * @throws InterruptedException if any */ @Test @JunitPerfConfig(duration = 1000) public void helloWorldTest() throws InterruptedException { System.out.println("hello world"); Thread.sleep(20); } }
再看一下 junit5 的写法:
public class HelloWorldTest { @JunitPerfConfig(duration = 1000) public void helloTest() throws InterruptedException { Thread.sleep(100); System.out.println("Hello Junit5"); } }
JunitPerfRule 竟然神奇的消失了?这一切是怎么做到的呢?
让我们一起揭开 junit5 神秘的面纱。
我们只是指定了一个简单的 @JunitPerfConfig
注解,那么问题一定就出在这个注解里。
定义如下:
import java.lang.annotation.*; /** * 执行接口 * 对于每一个测试方法的条件配置 * @author bbhou * @version 1.0.0 * @since 1.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @ExtendWith(PerfConfigProvider.class) @TestTemplate public @interface JunitPerfConfig { // 属性省略 }
@Retention
和 @Target
属于 java 中的常规注解,此处不做赘述。
我们重点看一下剩余的两个注解。
我们以前在写单元测试的时候,都会写一个 @Test
注解,你会发现 junit5 中连这个注解都省略了。
那么,他去哪里了呢?
答案就是 @TestTemplate
声明的注解,就是用来标识这个方法是单元测试的方法,idea 也会认的,这一点非常的灵活强大。
这个注解,给我们的注解进行了赋能。
看名字,就是一个拓展,拓展的实现,就是我们指定的类 PerfConfigProvider
我们来看一下 PerfConfigProvider 的实现。
public class PerfConfigProvider implements TestTemplateInvocationContextProvider { @Override public boolean supportsTestTemplate(ExtensionContext context) { return context.getTestMethod() .filter(m -> AnnotationSupport.isAnnotated(m, JunitPerfConfig.class)) .isPresent(); } @Override public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) { return Stream.of(new PerfConfigContext(context)); } }
实现非常简单,首先是一个过滤。
只有定义了 @JunitPerfConfig
注解的方法,才会生效。
下面就是我们自定义实现的上下文 PerfConfigContext。
PerfConfigContext 实现了 TestTemplateInvocationContext,并且对原生的 ExtensionContext 进行了简单的封装。
public class PerfConfigContext implements TestTemplateInvocationContext { // 省略内部属性 @Override public List<Extension> getAdditionalExtensions() { return Collections.singletonList( (TestInstancePostProcessor) (testInstance, context) -> { final Class clazz = testInstance.getClass(); // Group test contexts by test class ACTIVE_CONTEXTS.putIfAbsent(clazz, new ArrayList<>()); EvaluationContext evaluationContext = new EvaluationContext(testInstance, method, DateUtil.getCurrentDateTimeStr()); evaluationContext.loadConfig(perfConfig); evaluationContext.loadRequire(perfRequire); StatisticsCalculator statisticsCalculator = perfConfig.statistics().newInstance(); Set<Reporter> reporterSet = getReporterSet(); ACTIVE_CONTEXTS.get(clazz).add(evaluationContext); try { new PerformanceEvaluationStatement(evaluationContext, statisticsCalculator, reporterSet, ACTIVE_CONTEXTS.get(clazz), clazz).evaluate(); } catch (Throwable throwable) { throw new JunitPerfRuntimeException(throwable); } } ); } }
写到这里,我们就会发现又回到了和 junit4 相似的地方。
不明白的小伙伴可以去看一下原来的实现,这里不做赘述。
剩下的部分,和原来 junit4 的实现都是一致的。
可以发现 junit5 为我们提供的拓展能力更加强大灵活,他可以让我们定义属于自己的注解。
这个注解用起来让用户和使用原有的 junit5 注解没有什么区别。
不得不感慨一句,长江后浪推前浪,前浪死在沙滩上。
https://github.com/houbb/junitperf
https://github.com/junit-team/junit4/wiki/Rules