Mockito為什么不能mock靜態方法


因為Mockito使用繼承的方式實現mock的,用CGLIB生成mock對象代替真實的對象進行執行,為了mock實例的方法,你可以在subclass中覆蓋它,而static方法是不能被子類覆蓋的,所以Mockito不能mock靜態方法。

但PowerMock可以mock靜態方法,因為它直接在bytecode上工作。

PowerMock是一個JUnit擴展,它利用了EasyMock和Mockito模擬靜態method的方法對Java中的靜態method進行Mock,而且它還有更多的功能(詳見github/powermock)。

首先我們設計一個靜態類如下(Utility.java):

public class Utility { public static <T> boolean listIsNullOrEmpty(List<T> objectList) { return objectList == null || objectList.isEmpty(); } public static <T> boolean listIsNotNullOrEmpty(List<T> objectList) { return objectList != null && !objectList.isEmpty(); } } 

  被測試類如下(UtilityHelper.java):

public class UtilityHelper { public int sum(List<Integer> dataLst) { if (Utility.listIsNullOrEmpty(dataLst)) { return 0; } int total = 0; for (Integer data : dataLst) { total += data; } return total; } public int product(List<Integer> dataList) { int total = 1; if (Utility.listIsNotNullOrEmpty(dataList)) { for (Integer data : dataList) { total *=data; } } return total; } } 

  在被測試類中分別定義了兩個方法,分別調用了Utility類里面的兩個靜態方法,下面我們通過對這兩個方法進行測試,來介紹下使用Powermock對靜態方法進行mock的各種用法。

  測試類如下(UtilityHelperTest.java):

@RunWith(PowerMockRunner.class) @PrepareForTest({Utility.class}) public class UtilityHelperTest { private UtilityHelper utilityHelper; private List<Integer> dataList; @Before public void setUp() { PowerMockito.mockStatic(Utility.class); dataList = new ArrayList<Integer>(); dataList.add(1); dataList.add(2); dataList.add(3); utilityHelper = new UtilityHelper(); } @Test public void testSum_1() { PowerMockito.when(Utility.listIsNullOrEmpty(Mockito.anyList())).thenReturn(true); int sum = utilityHelper.sum(dataList); Assert.assertEquals(0, sum); PowerMockito.verifyStatic(Mockito.times(1)); Utility.listIsNullOrEmpty(Mockito.anyList()); } @Test public void testSum_2() { PowerMockito.when(Utility.listIsNullOrEmpty(Mockito.anyList())).thenReturn(false); int sum = utilityHelper.sum(dataList); Assert.assertEquals(6, sum); } @Test public void testProduct_1() { int product = utilityHelper.product(dataList); Assert.assertEquals(1, product); } @Test public void testProduct_2() { PowerMockito.spy(Utility.class); int product = utilityHelper.product(dataList); Assert.assertEquals(6, product); } @Test public void testProduct_3() { PowerMockito.when(Utility.listIsNotNullOrEmpty(Mockito.anyList())).thenCallRealMethod(); int product = utilityHelper.product(dataList); Assert.assertEquals(6, product); } } 
  1. 如果想要對某個類的靜態方法進行mock,則必須在PrepareForTest后面加上相應的類名, 比如此例的Utility.class。

  2. 在對該類的某個方法進行mock之前,還必須先對整個類進行mock:

PowerMockito.mockStatic(Utility.class); 
  1. 在testSum_1方法中,我們對listIsNullOrEmpty進行了mock:
PowerMockito.when(Utility.listIsNullOrEmpty(Mockito.anyList())).thenReturn(true); 

可以看到雖然入參非空,但是由於返回值返回了true,使得調用sum方法返回的值是0。
  另外,如果我們想要驗證某靜態方法是否被調用,或者被調用了幾次,我們可以用如下方式驗證:

PowerMockito.verifyStatic(Mockito.times(1)); Utility.listIsNullOrEmpty(Mockito.anyList()); 

  先使用verifyStatic方法表明要驗證靜態方法,可以帶參數,也可以不帶參數,其參數可以使用Mockito的times方法或never方法來表示其調用次數。下面緊跟着的一行則表示要驗證的是哪個已經mock的靜態方法。

  1. 在test_sum2方法中,由於我們mock的返回值為false,所以調用sum方法返回的是實際值。
  2. 在test_product1中,我們可以看到並沒有對product中調用的listIsNotNullOrEmpty進行mock,那么為什么返回值是 1 呢?
      這個主要是因為我們在setup方法中對使用mockStatic方法對Utility.class進行了mock,那么此時該類中的所有方法實際上都已經被mock了,如果沒有對某個方法進行具體mock返回值,則調用該方法時,會直接返回對應返回類型的默認值,並不會執行真正的方法。此例對於listIsNotNullOrEmpty方法來說,其返回類型為boolean型,其默認值為false,所以product方法返回 1

  那么如果我們想對已經mock的類的某個方法調用真實的方法,而不是調用mock方法,那么該如何處理呢?此處我們介紹兩種實現:

  1. 在test_product2中,我們看到相對test_product1來說,多了一行:
PowerMockito.spy(Utility.class); 

  加了上面一行后,雖然也沒有對listIsNotNullOrEmpty進行mock,但此時返回值是真正的值,說明沒有調用默認的mock方法。使用spy后,雖然已經對該類做了mockStatic處理,但此時該類中的所有方法仍然都會調用真實的方法,而不是默認的mock方法。這種用法主要適用於只想要對某個類的少量方法進行mock,其他方法仍然執行真正的方法,平常寫時,可以緊跟在mockStatic方法后。

  1. 我們在看下test_product3中,該方法相對test_product1中,多了一行:
PowerMockito.when(Utility.listIsNotNullOrEmpty(Mockito.anyList())).thenCallRealMethod(); 

  此行的含義就是調用到mock類的該方法執行真正的方法,而不是mock方法。這種方式就是需要對每個要執行的方法都要進行相應的mock處理。
  上述兩種方式,可以根據自己的需要進行選擇使用哪一種。但是一定要記得,只要使用了mockStatic某類時,該類中的所有方法就已經都默認被mock了, 在調用該類的方法時,必須根據具體方法進行相應的處理。
  另外,重要的事說三遍: 如果要mock靜態方法,必須要在PrepareForTest后面加上該方法所在的類





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM