使用Mockito模擬靜態方法

1.概述

通常,在編寫單元測試時,我們會遇到需要模擬靜態方法的情況。在Mockito的3.4.0版本之前,不可能直接模擬靜態方法–僅在PowerMockito的幫助下

在本教程中,我們將研究如何使用最新版本的Mockito模擬靜態方法。要了解有關使用Mockito進行測試的更多信息,請查看我們全面的Mockito系列。

2.一個簡單的靜態實用程序類

在整個教程中,我們測試的重點將是一個簡單的靜態實用程序類:

public class StaticUtils {



 private StaticUtils() {}



 public static List<Integer> range(int start, int end) {

 return IntStream.range(start, end)

 .boxed()

 .collect(Collectors.toList());

 }



 public static String name() {

 return "1ju.org";

 }

 }

為了演示的目的,我們有一個方法帶有一些參數,而另一種方法僅返回String

3.依存關係

讓我們開始吧,向我們的pom.xml mockito-inline依賴項:

<dependency>

 <groupId>org.mockito</groupId>

 <artifactId>mockito-inline</artifactId>

 <version>3.8.0</version>

 <scope>test</scope>

 </dependency>

值得注意的是,在某些時候,根據文檔,此功能很可能已集成到更熟悉的mockito-core依賴中。

4.快速測試靜態方法

一般來說,有人可能會說,在編寫乾淨的面向對象的代碼時,我們不需要模擬靜態類。這通常可能暗示我們的應用程序中存在設計問題或代碼異味。

首先,因為依賴於靜態方法的類具有緊密的耦合,其次,它幾乎總是導致難以測試的代碼。理想情況下,類不應負責獲取其依賴項,並且如果可能的話,應該從外部注入它們。

因此,始終值得研究是否可以重構代碼以使其更具可測試性。當然,這並非總是可能的,有時我們不得不模擬靜態方法。

5.模擬一個無參數的靜態方法

讓我們繼續前進,看看如何從StaticUtils類中name

@Test

 void givenStaticMethodWithNoArgs_whenMocked_thenReturnsMockSuccessfully() {

 assertThat(StaticUtils.name()).isEqualTo("Baeldung");



 try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {

 utilities.when(StaticUtils::name).thenReturn("Eugen");

 assertThat(StaticUtils.name()).isEqualTo("Eugen");

 }



 assertThat(StaticUtils.name()).isEqualTo("1ju.org");

 }

如前所述,自Mockito 3.4.0起,我們可以使用Mockito.mockStatic( Class<T> classToMock )方法來模擬對靜態方法調用的調用。此方法MockedStatic對象,這是一個有範圍的模擬對象。

因此,在上面的單元測試中, utilities變量表示具有線程局部顯式作用域的模擬。重要的是要注意,作用域模擬必須由激活該模擬的實體關閉。這就是為什麼我們在try-with-resources構造中定義模擬程序的原因,以便當我們完成作用域塊時自動關閉模擬程序。

這是一個特別不錯的功能,因為它可以確保我們的靜態模擬仍然是臨時的。眾所周知,如果在測試運行過程中使用靜態方法調用,由於運行測試的並發性和順序性,這很可能對測試結果造成不利影響。

最重要的是,另一個好處是我們的測試仍然可以超級快速地運行,因為Mockito不需要為每個測試都替換類加載器。

在我們的示例中,我們通過在作用域塊之前和之後檢查靜態方法name返回實值來再次重申這一點。

6.用參數模擬靜態方法

現在,讓我們看看需要模擬帶有參數的方法時的另一個常見用例:

@Test

 void givenStaticMethodWithArgs_whenMocked_thenReturnsMockSuccessfully() {

 assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);



 try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {

 utilities.when(() -> StaticUtils.range(2, 6))

 .thenReturn(Arrays.asList(10, 11, 12));



 assertThat(StaticUtils.range(2, 6)).containsExactly(10, 11, 12);

 }



 assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);

 }

如我們所見,除了這次,我們使用了與lambda表達式相同的方法,除了這次,我們在when子句中使用了lambda表達式。非常簡單!

7.結論

在這篇快速的文章中,我們已經看到了一些示例,這些示例說明瞭如何使用Mockito模擬靜態方法。總之,Mockito通過一個較小的lambda為狹窄的模擬靜態對象提供了一種範圍更廣的解決方案。