知識庫 / Spring / Spring Boot RSS 訂閱

SpringRunner 與 MockitoJUnitRunner

Spring Boot,Testing
HongKong
8
11:51 AM · Dec 06 ,2025

1. 概述

JUnit 是 Java 中最流行的單元測試框架之一。 此外,Spring Boot 將其作為默認的測試依賴項提供給其應用程序。

在本教程中,我們將比較兩個 JUnit 運行器:<em >SpringRunner</em><em >MockitoJUnitRunner</em>。 我們將瞭解它們的用途以及它們之間的關鍵差異。

2. @RunWith@ExtendWith

在繼續之前,我們先回顧一下如何擴展基本 JUnit 功能或將其與其他庫集成。

JUnit 4 允許我們實現自定義 Runner 類,這些類負責通過應用額外功能運行測試。要調用自定義的 Runner,我們使用 @RunWith 註解來標記測試類:

@RunWith(CustomRunner.class)
class JUnit4Test {
    // ...
}

正如我們所知,JUnit 4 現在處於停用狀態,已被 JUnit 5 取代。 新版本帶來了全新的引擎以及重寫的 API。 它還改變了擴展模型的概念。 現在,我們不再需要實現自定義的 RunnerRule 類,而是可以使用 Extension API,並使用 @ExtendWith 註解:

@ExtendWith(CustomExtensionOne.class)
@ExtendWith(CustomExtensionTwo.class)
class JUnit5Test {
    // ...
}

與之前的跑步者模型不同,我們可以為單個類提供多個擴展。之前交付的跑步者模型也已針對其擴展版本進行了重寫。

3. Spring 示例應用程序

為了更好地理解,我們來介紹一個簡單的 Spring Boot 應用程序——一個將給定字符串映射為大寫的轉換器。

讓我們從數據提供者的實現開始:

@Component
public class DataProvider {
    
    private final List<String> memory = List.of("baeldung", "java", "dummy");
    
    public Stream<String> getValues() {
        return memory.stream();
    }
}

我們剛剛創建了一個包含硬編碼字符串值的 Spring 組件。 此外,它還提供了一個方法,用於流式傳輸這些字符串。

其次,讓我們實現一個服務類,用於轉換這些值:

@Service
public class StringConverter {
    
    private final DataProvider dataProvider;

    @Autowired
    public StringConverter(DataProvider dataProvider) {
        this.dataProvider = dataProvider;
    }
    
    public List<String> convert() {
        return dataProvider.getValues().map(String::toUpperCase).toList();
    }
}

這是一個簡單的 Bean,它從先前創建的 DataProvider 中獲取數據並應用大寫映射。

現在,我們可以使用我們的應用程序來創建 JUnit 測試。我們將看到 SpringRunnerMockitoJUnitRunner 類之間的差異。

4. MockitoJUnitRunner

我們知道,Mockito 是一個用於伴隨其他測試框架使用的 Mocking 框架,用於返回虛假數據並避免外部依賴。該庫為我們提供 MockitoJUnitRunner – 一個專門為 JUnit 4 設計的運行器,用於集成 Mockito 並充分利用該庫的功能。

現在,讓我們為 StringConverter 創建第一個測試:

public class StringConverterTest {
    @Mock
    private DataProvider dataProvider;

    @InjectMocks
    private StringConverter stringConverter;
    
    @Test
    public void givenStrings_whenConvert_thenReturnUpperCase() {
        Mockito.when(dataProvider.getValues()).thenReturn(Stream.of("first", "second"));

        val result = stringConverter.convert();

        Assertions.assertThat(result).contains("FIRST", "SECOND");
    }
}

我們剛剛模擬了 DataProvider 以返回兩個字符串。但是,如果運行它,測試就會失敗:

java.lang.NullPointerException: Cannot invoke "DataProvider.getValues()" because "this.dataProvider" is null

那是因為我們的 Mock 沒有正確初始化。<em @Mock@</em><em @InjectMocks@</em> 註解目前沒有任何作用。我們可以通過實現 <em init()@</em> 方法來解決這個問題:

@Before
public void init() {
    MockitoAnnotations.openMocks(this);
}

如果不想使用註解,我們也可以通過程序方式創建和注入模擬對象:

@Before
public void init() {
    dataProvider = Mockito.mock(DataProvider.class);
    stringConverter = new StringConverter(dataProvider);
}

我們已經通過使用 Mockito API 程序化地初始化了 mock 對象。現在,測試正常工作,我們的斷言也通過了。

接下來,讓我們回到我們第一次版本,刪除 init() 方法,並使用 MockitoJUnitRunner 註解該類:

@RunWith(MockitoJUnitRunner.class)
public class StringConverterTest {
    // ...
}

再次測試成功。我們調用了一個自定義的運行器,它負責管理我們的模擬對象。我們無需手動初始化它們。

總結一下,MockitoJUnitRunner 是專門為 Mockito 框架設計的運行器。它負責初始化 @Mock@Spy@InjectMock 註解,因此無需使用 MockitoAnnotations.openMocks() 進行顯式操作。 此外,它在每個測試方法後檢測未使用的樁對象並驗證 Mockito 的使用,就像 Mockito.validateMockitoUsage() 所做的那樣。

請記住,所有運行器最初都是為 JUnit 4 設計的。 如果我們想使用 Mockito 註解與 JUnit 5 配合使用,可以使用 MockitoExtension

@ExtendWith(MockitoExtension.class)
public class StringConverterTest {
    // ...
}

此擴展將 MockitoJUnitRunner 的功能移植到新的擴展模型中。

5. SpringRunner

如果深入分析我們的測試,我們會發現,儘管使用了 Spring,我們實際上並沒有啓動任何 Spring 容器。現在,讓我們嘗試修改我們的示例並初始化 Spring 上下文。

首先,代替 MockitoJUnitRunner,我們只需將其替換為 SpringRunner 類並查看結果:

@RunWith(SpringRunner.class)
public class StringConverterTest {
    // ...
}

如前所述,測試通過,並且 Mock 對象已正確初始化。 此外,Spring Context 也在運行中。 可以得出結論,SpringRunner 不僅能夠啓用 Mockito 註解,就像 MockitoJUnitRunner 也能做到,還能夠初始化 Spring Context。

當然,我們尚未充分利用 Spring 的潛力,在我們的測試中。 相對於構造新的對象,我們可以將它們注入為 Spring Bean。 就像我們所知道的,Spring 測試模塊默認集成了 Mockito 集成,這也提供了 @MockBean@SpyBean 註解——將 Mock 對象和 Bean 功能集成在一起。

讓我們重寫我們的測試:

@ContextConfiguration(classes = StringConverter.class)
@RunWith(SpringRunner.class)
public class StringConverterTest {
    @MockBean
    private DataProvider dataProvider;

    @Autowired
    private StringConverter stringConverter;

    // ...
}

我們已將 @Mock 註解替換為 @MockBean,並將其放置在 DataProvider 對象旁邊。它仍然是一個 Mock 對象,但現在也可以用作 Bean。我們還通過使用 @ContextConfiguration 類註解配置了我們的 Spring Context,並注入了 StringConverter。 結果,測試仍然通過,但現在它同時使用了 Spring Bean 和 Mockito。

總而言之,SpringRunner 是為 JUnit 4 創建的自定義 Runner,它提供了 Spring TestContext Framework 的功能。 鑑於 Mockito 是與 Spring 堆棧集成的默認 Mocking 框架,該 Runner 提供了 MockitoJUnitRunner 提供的全部支持。 有時,我們還會遇到 SpringJUnit4ClassRunner,它是該 Runner 的別名, 並且我們可以輪流使用它們。

如果我們正在尋找 SpringRunner 的擴展版本,則應使用 SpringExtension

@ExtendWith(SpringExtension.class)
public class StringConverterTest {
    // ...
}

作為 JUnit 5 是 Spring Boot 棧中的默認測試框架,該擴展已與許多測試切片註解集成,包括

6. 結論

在本文中,我們學習了 <em>SpringRunner</em><em>MockitoJUnitRunner</em> 之間的區別。

我們首先回顧了 JUnit 4 和 JUnit 5 中使用的擴展模型。JUnit 4 使用專用運行器,而 JUnit 5 支持擴展。同時,我們可以提供單個運行器或多個擴展。

然後,我們研究了 <em>MockitoJUnitRunner</em>,它 允許 Mockito 框架在我們的 JUnit 4 測試中得到支持。 總體而言,我們可以通過專用 <em>@Mock</em><em>@Spy</em><em>@InjectMocks</em> 註解來配置我們的 Mock,而無需初始化方法。

最後,我們討論了 <em>SpringRunner</em>,它 釋放了 Mockito 和 Spring 框架之間協同工作的所有優勢。 它不僅支持基本的 Mockito 註解,還支持 Spring 註解:<em>@MockBean</em><em>@SpyBean</em>。 這樣構造的 Mock 可以使用 Spring Context 注入。

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

發佈 評論

Some HTML is okay.