基於 junit5 實現 junitperf 原始碼分析

老馬嘯西風發表於2021-07-24

前言

上一節介紹了基於 junit4 實現 junitperf,但是可以發現定義變數的方式依然不夠優雅。

那可以讓使用者使用起來更加自然一些嗎?

有的,junit5 為我們帶來了更加強大的功能。

擴充閱讀:

淺談效能測試

基於 junit4 分析 junitperf 原始碼,junit4 99% 的人都不知道的祕密!

junit5

沒有對比,就沒有傷害

我們首先回顧一下 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 神祕的面紗。

Junit5 更加強大的特性

@JunitPerfConfig

我們只是指定了一個簡單的 @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 中的常規註解,此處不做贅述。

我們重點看一下剩餘的兩個註解。

@TestTemplate

我們以前在寫單元測試的時候,都會寫一個 @Test 註解,你會發現 junit5 中連這個註解都省略了。

那麼,他去哪裡了呢?

答案就是 @TestTemplate 宣告的註解,就是用來標識這個方法是單元測試的方法,idea 也會認的,這一點非常的靈活強大。

@ExtendWith

這個註解,給我們的註解進行了賦能。

看名字,就是一個擴充,擴充的實現,就是我們指定的類 PerfConfigProvider

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

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 註解沒有什麼區別。

不得不感慨一句,長江後浪推前浪,前浪死在沙灘上。

參考資料

github.com/houbb/junit…

github.com/junit-team/…

相關文章