知識庫 / Spring RSS 訂閱

Spring 測試執行監聽器

Spring,Testing
HongKong
4
01:08 PM · Dec 06 ,2025

1. 概述

通常,我們使用 JUnit 註解,如 <em @BeforeEach, @AfterEach, @BeforeAll, @AfterAll</em>,來編排測試的生命週期,但有時這還不夠——尤其是在我們使用 Spring 框架時。

這正是 Spring 的 <em TestExecutionListener</em>> 發揮作用的地方。

在本教程中,我們將看到 <em TestExecutionListener</em>> 提供什麼,Spring 提供的默認監聽器以及如何實現自定義 <em TestExecutionListener</em>>

2. TestExecutionListener 接口

首先,讓我們訪問 TestExecutionListener 接口:

public interface TestExecutionListener {
    default void beforeTestClass(TestContext testContext) throws Exception {};
    default void prepareTestInstance(TestContext testContext) throws Exception {};
    default void beforeTestMethod(TestContext testContext) throws Exception {};
    default void afterTestMethod(TestContext testContext) throws Exception {};
    default void afterTestClass(TestContext testContext) throws Exception {};
}

此接口的實現可以接收在不同測試執行階段發生的事件。因此,接口中的每個方法都會接收一個 TestContext 對象。

TestContext 對象包含 Spring 上下文以及目標測試類和方法的相關信息。這些信息可用於修改測試的行為或擴展其功能。

現在,讓我們快速瞭解一下這些方法:

  • afterTestClass – 在測試類內執行所有測試後,對測試類進行後處理 即在所有測試執行完畢後
  • afterTestExecution – 在提供的測試上下文中,在測試方法執行完畢後立即對測試進行後處理 即在測試方法執行完畢後立即
  • afterTestMethod – 對測試進行後處理,在底層測試框架的 before-lifecycle 回調執行完畢後
  • beforeTestClass – 在測試類內執行所有測試之前,對測試類進行預處理 即在所有測試執行之前
  • beforeTestExecution – 在提供的測試上下文中,在測試方法執行之前立即對測試進行預處理 即在測試方法執行之前立即
  • beforeTestMethod – 對測試進行預處理,在底層測試框架的 before-lifecycle 回調執行完畢之前
  • prepareTestInstance – 準備提供的測試上下文中的測試實例

值得注意的是,此接口為所有方法提供了空默認實現。因此,具體的實現可以選擇覆蓋適合其任務的那些方法。

3. Spring 的默認 TestExecutionListener

默認情況下,Spring 提供了一些 TestExecutionListener 實現。

我們快速瞭解一下這些實現:

  • ServletTestExecutionListener – 為 WebApplicationContext 配置 Servlet API 模擬對象
  • DirtiesContextBeforeModesTestExecutionListener – 處理 @DirtiesContext 註解的“before” 模式
  • DependencyInjectionTestExecutionListener – 為測試實例提供依賴注入
  • DirtiesContextTestExecutionListener – 處理 @DirtiesContext 註解的“after” 模式
  • TransactionalTestExecutionListener – 提供帶有默認回滾語義的事務測試執行
  • SqlScriptsTestExecutionListener – 運行使用 @Sql 註解配置的 SQL 腳本

這些監聽器在列表中列出的順序中預先註冊。當我們創建自定義 TestExecutionListener 時,我們會更詳細地瞭解這個順序。

4. 使用自定義 TestExecutionListener

現在,讓我們定義一個自定義的 TestExecutionListener

public class CustomTestExecutionListener implements TestExecutionListener, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(CustomTestExecutionListener.class);
    
    public void beforeTestClass(TestContext testContext) throws Exception {
        logger.info("beforeTestClass : {}", testContext.getTestClass());
    }; 
    
    public void prepareTestInstance(TestContext testContext) throws Exception {
        logger.info("prepareTestInstance : {}", testContext.getTestClass());
    }; 
    
    public void beforeTestMethod(TestContext testContext) throws Exception {
        logger.info("beforeTestMethod : {}", testContext.getTestMethod());
    }; 
    
    public void afterTestMethod(TestContext testContext) throws Exception {
        logger.info("afterTestMethod : {}", testContext.getTestMethod());
    }; 
    
    public void afterTestClass(TestContext testContext) throws Exception {
        logger.info("afterTestClass : {}", testContext.getTestClass());
    }

    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    };
}

為了簡化起見,這個類僅用於記錄一些 TestContext 的信息。

4.1. 使用 @TestExecutionListeners</em/> 註冊自定義監聽器

現在,讓我們在測試類中使用這個監聽器。要做到這一點,我們將通過使用 @TestExecutionListeners</em/> 註解來註冊它:

@RunWith(SpringRunner.class)
@TestExecutionListeners(value = {
  CustomTestExecutionListener.class,
  DependencyInjectionTestExecutionListener.class
})
@ContextConfiguration(classes = AdditionService.class)
public class AdditionServiceUnitTest {
    // ...
}

需要注意的是,使用註解將取消所有默認監聽器的註冊。因此,我們明確添加了 DependencyInjectionTestExecutionListener,以便我們在測試類中使用自動裝配。

如果需要使用其他默認監聽器,則必須明確指定它們。但是,我們也可以使用註解的 mergeMode 屬性。

@TestExecutionListeners(
  value = { CustomTestExecutionListener.class }, 
  mergeMode = MergeMode.MERGE_WITH_DEFAULTS)

這裏,MERGE_WITH_DEFAULTS 表示本地聲明的監聽器應與默認監聽器合併。

現在,當我們運行上述測試時,監聽器將記錄它接收到的每個事件:

[main] INFO  o.s.t.c.s.DefaultTestContextBootstrapper - Using TestExecutionListeners: 
[com.baeldung.testexecutionlisteners.CustomTestExecutionListener@38364841, 
org.springframework.test.context.support.DependencyInjectionTestExecutionListener@28c4711c]
[main] INFO  c.b.t.CustomTestExecutionListener - beforeTestClass : 
class com.baeldung.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
[main] INFO  c.b.t.CustomTestExecutionListener - prepareTestInstance : 
class com.baeldung.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
[main] INFO  o.s.c.s.GenericApplicationContext - 
Refreshing org.springframework.context.support.GenericApplicationContext@7d68ef40: startup date [XXX]; 
root of context hierarchy
[main] INFO  c.b.t.CustomTestExecutionListener - beforeTestMethod : 
public void com.baeldung.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
.whenValidNumbersPassed_thenReturnSum()
[main] INFO  c.b.t.CustomTestExecutionListener - afterTestMethod : 
public void com.baeldung.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest
.whenValidNumbersPassed_thenReturnSum()
[main] INFO  c.b.t.CustomTestExecutionListener - afterTestClass : 
class com.baeldung.testexecutionlisteners.TestExecutionListenersWithoutMergeModeUnitTest

4.2. 默認 <emTestExecutionListener</em>> 實現的自動發現

使用 <emTestExecutionListener</em>> 註冊監聽器,如果僅在少量測試類中使用,則適用。但是,將其添加到整個測試套件中可能會變得繁瑣。

我們可以通過利用 <emSpringFactoriesLoader</em>> 機制的自動發現 <emTestExecutionListener</em>> 實現來解決這個問題。

<emspring-test</em>> 模塊在它的 <emMETA-INF/spring.factories</em>> 屬性文件中,在 <emorg.springframework.test.context.TestExecutionListener</em> 鍵下聲明瞭所有核心默認監聽器。 類似地,我們可以通過在自己的 <emMETA-INF/spring.factories</em>> 屬性文件中使用上述鍵來註冊我們的自定義監聽器

org.springframework.test.context.TestExecutionListener=\
com.baeldung.testexecutionlisteners.CustomTestExecutionListener

4.3. 使用默認 <em>TestExecutionListener</em> 實現

當 Spring 通過 <em>SpringFactoriesLoader</em> 機制發現默認的 <em>TestExecutionListener</em> 實現時,它將使用 Spring 的 <em>AnnotationAwareOrderComparator</em> 對其進行排序。 這尊重 Spring 的 <em>Ordered</em> 接口和 @Order 註解,用於排序。

請注意,所有由 Spring 提供的默認 <em>TestExecutionListener</em> 實現都實現了 <em>Ordered</em> 接口,並具有適當的值。 因此,我們必須確保我們的自定義 <em>TestExecutionListener</em> 實現已使用正確的順序進行註冊。 因此,我們已在自定義監聽器中實現了 <em>Ordered</em>

public class CustomTestExecutionListener implements TestExecutionListener, Ordered {
    // ...
    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    };
}

但是,我們也可以使用 @Order 註解代替。

5. 結論

在本文中,我們學習瞭如何實現自定義的TestExecutionListener。我們還考察了 Spring 框架提供的默認監聽器。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.