最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JUnit 5 中的 @ClassTemplate 實(shí)戰(zhàn)攻略

 更新時(shí)間:2025年12月17日 08:45:32   作者:程序猿DD  
這篇文章主要介紹了JUnit 5 中的 @ClassTemplate 實(shí)戰(zhàn)指南,在本文中,我們從基礎(chǔ)概念出發(fā),進(jìn)一步深入了解@ClassTemplate的使用方式,重點(diǎn)考察了提供者如何為單個(gè)測(cè)試類提供多個(gè)執(zhí)行上下文,需要的朋友可以參考下

當(dāng)你在本地、測(cè)試環(huán)境和 CI 中跑同一組測(cè)試時(shí),是否遇到過(guò)這樣的困惑:同一段業(yè)務(wù)邏輯在不同配置、不同 Locale 下的表現(xiàn)不盡相同,但你又不想為每種場(chǎng)景復(fù)制一堆幾乎一樣的測(cè)試類?如果把所有分支邏輯都塞進(jìn)一個(gè)測(cè)試方法里,又會(huì)讓測(cè)試變得臃腫難以維護(hù)。有沒(méi)有一種方式,可以讓測(cè)試代碼保持簡(jiǎn)潔,卻能優(yōu)雅地在多種“環(huán)境切面”下重復(fù)執(zhí)行整套測(cè)試?這正是 JUnit 5 中 @ClassTemplate 想要解決的問(wèn)題。本文就從這個(gè)現(xiàn)實(shí)場(chǎng)景出發(fā),帶你深入理解 Class Template 的執(zhí)行機(jī)制、擴(kuò)展點(diǎn)設(shè)計(jì)以及一個(gè)實(shí)用的多 Locale 示例。

1. 引言

有些測(cè)試需要在不同的環(huán)境中運(yùn)行。@ClassTemplate 注解可以幫我們做到這一點(diǎn):它會(huì)讓整個(gè)測(cè)試類在多種不同配置下被重復(fù)執(zhí)行。

在這篇教程中,我們會(huì)先討論為什么會(huì)有“類模板(Class Template)”這種機(jī)制,以及 JUnit 是如何執(zhí)行它們的;接著會(huì)看看它在整體執(zhí)行模型中的位置;最后,我們會(huì)拆解類模板的結(jié)構(gòu)、背后的提供者(provider),并通過(guò)一個(gè)示例,在不復(fù)制任何測(cè)試代碼的前提下,讓同一個(gè)測(cè)試類在多個(gè) Locale 環(huán)境下運(yùn)行。

2. 什么是 @ClassTemplate

簡(jiǎn)單回顧一下,@ClassTemplate 會(huì)把一個(gè)測(cè)試類變成“模板類”,讓它按照不同的調(diào)用上下文(invocation context)多次執(zhí)行。提供者負(fù)責(zé)提供這些上下文,每一個(gè)上下文都會(huì)觸發(fā)一次獨(dú)立的執(zhí)行,擁有各自的生命周期和擴(kuò)展。

在實(shí)踐中,這讓我們可以在不同環(huán)境或配置下多次運(yùn)行同一個(gè)測(cè)試類,同時(shí)保持測(cè)試代碼本身的簡(jiǎn)單性。我們可以改變運(yùn)行時(shí)的環(huán)境配置,而不用復(fù)制測(cè)試類,或者在單個(gè)測(cè)試方法里加入復(fù)雜的分支邏輯。

2.1. Class Template 如何執(zhí)行

一個(gè)類模板由兩部分組成:模板類本身,以及為其提供調(diào)用上下文的提供者。模板類在外觀上就像一個(gè)普通的 JUnit 測(cè)試類,但 @ClassTemplate 注解會(huì)告訴 JUnit 不要直接運(yùn)行它,而是等待提供者來(lái)定義該類的具體執(zhí)行方式。

一旦 JUnit 識(shí)別出某個(gè)類是類模板,提供者就會(huì)返回一個(gè)或多個(gè)上下文,每個(gè)上下文都定義了一次完整的執(zhí)行。對(duì)于每個(gè)上下文,JUnit 都會(huì)創(chuàng)建一個(gè)新的測(cè)試實(shí)例,應(yīng)用對(duì)應(yīng)的擴(kuò)展,并執(zhí)行生命周期方法和測(cè)試方法。這樣,測(cè)試類可以專注于業(yè)務(wù)邏輯本身,而由提供者來(lái)塑造運(yùn)行時(shí)環(huán)境。

2.2. Class Template 與 Method Template 對(duì)比

在繼續(xù)之前,值得先對(duì)比一下類模板和方法模板(method template)之間的區(qū)別。兩者都支持重復(fù)執(zhí)行,但關(guān)注的層級(jí)不同。方法模板會(huì)在不同輸入下重復(fù)執(zhí)行同一個(gè)測(cè)試方法;而類模板則會(huì)重復(fù)執(zhí)行整個(gè)測(cè)試類,包括它的生命周期回調(diào)、擴(kuò)展以及配置。

因此,當(dāng)變化點(diǎn)主要體現(xiàn)在整體環(huán)境層面——例如 Locale、特性開(kāi)關(guān)或系統(tǒng)級(jí)配置——而不是單個(gè)方法參數(shù)時(shí),類模板會(huì)更加合適。

3. 調(diào)用上下文提供者

接下來(lái),我們看看“調(diào)用上下文提供者(invocation context provider)”。這個(gè)擴(kuò)展負(fù)責(zé)為類模板提供執(zhí)行上下文。它需要實(shí)現(xiàn) ClassTemplateInvocationContextProvider 接口,該接口定義了兩個(gè)核心方法,用來(lái)決定提供者如何參與測(cè)試執(zhí)行。

下面我們分別來(lái)看。

3.1. supportsClassTemplate() 方法

在 JUnit 使用某個(gè)提供者之前,它會(huì)先檢查該提供者是否適用于當(dāng)前正在發(fā)現(xiàn)的測(cè)試類。這個(gè)檢查就是通過(guò) supportsClassTemplate() 方法完成的:

@Override
public boolean supportsClassTemplate(ExtensionContext context) {
    return context.getTestClass()
        .map(aClass -> aClass.isAnnotationPresent(ClassTemplate.class))
        .orElse(false);
}

JUnit 會(huì)對(duì)每一個(gè)已注冊(cè)的提供者調(diào)用這個(gè)方法。只有返回 true 的提供者才會(huì)對(duì)當(dāng)前類模板生效。通過(guò)這種機(jī)制,JUnit 可以避免提供者被意外激活,避免在無(wú)關(guān)測(cè)試上運(yùn)行擴(kuò)展,同時(shí)也允許多個(gè)提供者并存而互不干擾。

3.2. provideClassTemplateInvocationContexts() 方法

一旦某個(gè)提供者被激活,JUnit 就會(huì)調(diào)用 provideClassTemplateInvocationContexts(),以獲取描述模板執(zhí)行方式的上下文:

@Override
public Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(ExtensionContext context) {
    return Stream.of(invocationContext("A"), invocationContext("B"));
}

每一個(gè)上下文都代表了一次對(duì)測(cè)試類的完整執(zhí)行。單個(gè)提供者可以提供一個(gè)或多個(gè)上下文;如果同時(shí)有多個(gè)提供者處于激活狀態(tài),JUnit 會(huì)把它們提供的流拼接起來(lái)。每個(gè)上下文都可以添加自己的擴(kuò)展或配置,從而讓提供者可以對(duì)該次執(zhí)行的環(huán)境進(jìn)行精細(xì)控制。

從這里開(kāi)始,JUnit 會(huì)為每個(gè)上下文創(chuàng)建一個(gè)新的測(cè)試類實(shí)例,應(yīng)用對(duì)應(yīng)的擴(kuò)展,并完整運(yùn)行生命周期方法和測(cè)試方法各一次。

4. 實(shí)用示例

為了更直觀地理解這些概念,我們來(lái)構(gòu)造一個(gè)示例:編寫(xiě)一個(gè)測(cè)試,用來(lái)驗(yàn)證在多個(gè) JVM Locale 下的日期格式化邏輯。由于 Locale 會(huì)影響整個(gè)執(zhí)行環(huán)境,這類需求非常適合用類模板來(lái)實(shí)現(xiàn)。我們只保留一個(gè)測(cè)試類,然后讓提供者在不同配置下多次執(zhí)行它。

4.1. 日期格式化邏輯

首先,從一個(gè)小工具類開(kāi)始,它使用當(dāng)前 JVM 默認(rèn) Locale 來(lái)格式化日期。只要默認(rèn) Locale 發(fā)生變化,它的輸出就會(huì)隨之改變:

class DateFormatter {
    public String format(LocalDate date) {
        DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
            .withLocale(Locale.getDefault());
        return date.format(formatter);
    }
}

有了這個(gè)類之后,我們就可以在多種不同的配置下驗(yàn)證它的行為,而這些配置都由類模板來(lái)提供。

4.2. 提供者與擴(kuò)展

為了支撐上述需求,我們首先需要一個(gè)擴(kuò)展,用來(lái)在單次執(zhí)行期間設(shè)置默認(rèn) Locale:

class LocaleExtension implements BeforeEachCallback, AfterEachCallback {
    private final Locale locale;
    private Locale previous;
    @Override
    public void beforeEach(ExtensionContext context) {
        previous = Locale.getDefault();
        Locale.setDefault(locale);
    }
    @Override
    public void afterEach(ExtensionContext context) {
        Locale.setDefault(previous);
    }
}

這個(gè)擴(kuò)展會(huì)在每次測(cè)試之前暫時(shí)替換 JVM 的默認(rèn) Locale,并在測(cè)試結(jié)束后恢復(fù)原有值。在不同執(zhí)行之間唯一變化的,就是傳入該擴(kuò)展的 Locale 實(shí)例。

接下來(lái),提供者會(huì)通過(guò) provideClassTemplateInvocationContexts() 方法來(lái)提供不同的上下文。每個(gè)上下文都由 invocationContext() 方法創(chuàng)建,該方法通過(guò) getDisplayName() 指定顯示名,并通過(guò) getAdditionalExtensions() 安裝對(duì)應(yīng)的 LocaleExtension

class DateLocaleClassTemplateProvider implements ClassTemplateInvocationContextProvider {
    @Override
    public Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(ExtensionContext context) {
        return Stream.of(Locale.US, Locale.GERMANY, Locale.ITALY, Locale.JAPAN)
            .map(this::invocationContext);
    }
    private ClassTemplateInvocationContext invocationContext(Locale locale) {
        return new ClassTemplateInvocationContext() {
            @Override
            public String getDisplayName(int invocationIndex) {
                return "Locale: " + locale.getDisplayName();
            }
            @Override
            public List<Extension> getAdditionalExtensions() {
                return List.of(new LocaleExtension(locale));
            }
        };
    }
}

通過(guò)這樣的配置,我們就得到了互不相同的執(zhí)行環(huán)境,最終會(huì)對(duì)同一個(gè)測(cè)試類執(zhí)行四次測(cè)試。

4.3. Class Template 測(cè)試

此時(shí),類模板的整體配置已經(jīng)就位,我們就可以專注于編寫(xiě)一個(gè)測(cè)試方法了。JUnit 會(huì)通過(guò)前面配置好的提供者,為每個(gè)上下文執(zhí)行一次這個(gè)方法:

private final DateFormatter formatter = new DateFormatter();
@Test
void givenDefaultLocale_whenFormattingDate_thenMatchesLocalizedOutput() {
    LocalDate date = LocalDate.of(2025, 9, 30);
    DateTimeFormatter expectedFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
        .withLocale(Locale.getDefault());
    String expected = date.format(expectedFormatter);
    String formatted = formatter.format(date);
    LOG.info("Locale: {}, Expected: {}, Formatted: {}", Locale.getDefault(), expected, formatted);
    assertEquals(expected, formatted);
}

在每次執(zhí)行中,測(cè)試都會(huì)基于當(dāng)前默認(rèn) Locale 計(jì)算預(yù)期值,并與 DateFormatter 的輸出進(jìn)行比較。類模板和提供者負(fù)責(zé)在每次執(zhí)行之間切換環(huán)境設(shè)置,因此測(cè)試代碼本身可以保持簡(jiǎn)單、干凈,不需要任何分支邏輯。

4.4. 測(cè)試輸出

最后,當(dāng)我們運(yùn)行這組測(cè)試時(shí),同一個(gè)測(cè)試類會(huì)在每個(gè) Locale 下執(zhí)行一次,而每次的格式化結(jié)果都不相同:

Locale: en_US, Expected: September 30, 2025, Formatted: September 30, 2025
Locale: de_DE, Expected: 30. September 2025, Formatted: 30. September 2025
Locale: it_IT, Expected: 30 settembre 2025, Formatted: 30 settembre 2025
Locale: ja_JP, Expected: 2025年9月30日, Formatted: 2025年9月30日

可以看到,每一行都對(duì)應(yīng)于一個(gè)調(diào)用上下文。測(cè)試代碼在這些運(yùn)行之間完全沒(méi)有變化;變化的只是由提供者和擴(kuò)展配置出來(lái)的執(zhí)行環(huán)境。

5. 總結(jié)

在本文中,我們從基礎(chǔ)概念出發(fā),進(jìn)一步深入了 @ClassTemplate 的使用方式,重點(diǎn)考察了提供者如何為單個(gè)測(cè)試類提供多個(gè)執(zhí)行上下文。通過(guò) Locale 示例,我們看到提供者和擴(kuò)展可以在不修改測(cè)試代碼的前提下靈活地切換測(cè)試環(huán)境。這使得類模板成為處理全局設(shè)置或配置級(jí)行為測(cè)試的一種干凈而優(yōu)雅的解決方案。

到此這篇關(guān)于JUnit 5 中的 @ClassTemplate 實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)JUnit 5 @ClassTemplate內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java使用ChatGPT的API詳解

    Java使用ChatGPT的API詳解

    OpenAI API 幾乎可以應(yīng)用于任何涉及理解或生成自然語(yǔ)言或代碼的任務(wù)。本文將帶你們介紹Java如何使用ChatGPT的API,感興趣的同學(xué)可以跟著小編一起來(lái)學(xué)習(xí)
    2023-04-04
  • maven settings.xml文件的存放及配置(包含了配置阿里云鏡像)

    maven settings.xml文件的存放及配置(包含了配置阿里云鏡像)

    本文詳細(xì)解釋了Maven中settings.xml文件的存放位置,以及用戶級(jí)別和全局級(jí)別的區(qū)別,重點(diǎn)介紹了localRepository、交互模式、離線模式、插件組、代理設(shè)置、服務(wù)器認(rèn)證、鏡像列表和激活profiles的使用方法,感興趣的可以了解一下
    2025-09-09
  • Springboot實(shí)例講解實(shí)現(xiàn)寵物醫(yī)院管理系統(tǒng)流程

    Springboot實(shí)例講解實(shí)現(xiàn)寵物醫(yī)院管理系統(tǒng)流程

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Springboot實(shí)現(xiàn)寵物醫(yī)院綜合管理系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2022-06-06
  • spring boot 防止重復(fù)提交實(shí)現(xiàn)方法詳解

    spring boot 防止重復(fù)提交實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了spring boot 防止重復(fù)提交實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了spring boot 防止重復(fù)提交具體配置、實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下
    2019-11-11
  • Java中常用數(shù)據(jù)類型的輸入輸出詳解

    Java中常用數(shù)據(jù)類型的輸入輸出詳解

    本文主要介紹了Java中幾個(gè)常用的數(shù)據(jù)類型是如何輸入和輸出的,例如:Char型、int型、double型、數(shù)組、字符串等,對(duì)我們學(xué)習(xí)java有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)
    2021-12-12
  • 詳解springboot shiro jwt實(shí)現(xiàn)權(quán)限管理

    詳解springboot shiro jwt實(shí)現(xiàn)權(quán)限管理

    為什么使用jwt呢,因?yàn)榭梢酝ㄟ^(guò)URL,POST參數(shù)或者在HTTP header發(fā)送,因?yàn)閿?shù)據(jù)量小,傳輸速度也很快。本篇通過(guò)具體代碼來(lái)進(jìn)行詳情解析,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值
    2021-09-09
  • 詳解Java中如何定義和訪問(wèn)靜態(tài)字段和方法

    詳解Java中如何定義和訪問(wèn)靜態(tài)字段和方法

    在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,我們通常使用類來(lái)創(chuàng)建對(duì)象,類中有許多成員變量和方法,它們是直接屬于對(duì)象實(shí)例的,與之對(duì)應(yīng)的還有一類成員,那就是靜態(tài)成員,本文就來(lái)和大家聊聊Java是如何定義訪問(wèn)靜態(tài)成員的吧
    2023-05-05
  • Java中數(shù)組的定義與使用詳解

    Java中數(shù)組的定義與使用詳解

    這篇文章主要給大家介紹了關(guān)于Java中數(shù)組的定義與使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-11-11
  • java使用文件流實(shí)現(xiàn)查看下載次數(shù)

    java使用文件流實(shí)現(xiàn)查看下載次數(shù)

    這篇文章主要為大家詳細(xì)介紹了java使用文件流實(shí)現(xiàn)查看下載次數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • spring-session自定義序列化方式

    spring-session自定義序列化方式

    這篇文章主要介紹了spring-session自定義序列化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論

祁阳县| 稻城县| 屏山县| 塘沽区| 即墨市| 兴安县| 德江县| 丰原市| 武邑县| 鸡西市| 禄劝| 滦平县| 鄂尔多斯市| 青州市| 开封县| 六安市| 临城县| 德昌县| 景德镇市| 普宁市| 苍南县| 新巴尔虎左旗| 缙云县| 西和县| 团风县| 宜春市| 开鲁县| 孝感市| 历史| 米易县| 五寨县| 德州市| 横山县| 南丰县| 仁怀市| 长治县| 车险| 虹口区| 章丘市| 奉节县| 江川县|