簡體   English   中英

Mockito:試圖監視方法是調用原始方法

[英]Mockito: Trying to spy on method is calling the original method

我正在使用 Mockito 1.9.0。 我想在 JUnit 測試中模擬一個類的單個方法的行為,所以我有

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

問題是,在第二行中,實際上調用了myClassSpy.method1() ,從而導致異常。 我使用模擬的唯一原因是以后,無論何時myClassSpy.method1() ,都不會調用真正的方法,並且將返回myResults對象。

MyClass是一個接口,而myInstance是它的一個實現,如果這很重要的話。

我需要做什么來糾正這種間諜行為?

讓我引用官方文檔

監視真實物體的重要問題!

有時不可能將 when(Object) 用於 stubbing 間諜。 例子:

 List list = new LinkedList(); List spy = spy(list); // Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty) when(spy.get(0)).thenReturn("foo"); // You have to use doReturn() for stubbing doReturn("foo").when(spy).get(0);

在您的情況下,它類似於:

doReturn(resultsIWant).when(myClassSpy).method1();

就我而言,使用 Mockito 2.0,我必須將所有any()參數更改為nullable()以便存根真正的調用。

我的情況與接受的答案不同。 我試圖為不在該包中的實例模擬包私有方法

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

和測試類

package common;

public abstract class AnimalTest<T extends Animal> {
  @Before
  setup(){
    doNothing().when(getInstance()).packageProtected();
  }

  abstract T getInstance();
}

package instances;

class DogTest extends AnimalTest<Dog> {
  Dog getInstance(){
    return spy(new Dog());
  }

  @Test
  public void myTest(){}
}

編譯是正確的,但是當它嘗試設置測試時,它會調用真正的方法。

聲明方法protectedpublic 可以解決問題,但這不是一個干凈的解決方案。

Tomasz Nurkiewicz 的回答似乎並沒有說明全部!

NB Mockito 版本:1.10.19。

我非常喜歡 Mockito 新手,所以無法解釋以下行為:如果有專家可以改進這個答案,請隨意。

有問題的方法在這里, getContentStringValue ,是不是final不是static

這一行確實調用了原始方法getContentStringValue

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

這一行沒有調用原始方法getContentStringValue

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

由於我無法回答的原因,使用isA()會導致doReturn的預期(?)“不調用方法”行為失敗。

我們來看看這里涉及的方法簽名:它們都是Matchers static方法。 Javadoc 說兩者都返回null ,這本身有點難以理解。 據推測,作為參數傳遞的Class對象被檢查,但結果要么從未計算過,要么被丟棄。 鑒於null可以代表任何類,並且您希望模擬方法不被調用, isA( ... )any( ... )的簽名不能只返回null而不是泛型參數* <T> ?

反正:

public static <T> T isA(java.lang.Class<T> clazz)

public static <T> T any(java.lang.Class<T> clazz)

API 文檔對此沒有提供任何線索。 似乎也說需要這種“不調用方法”行為是“非常罕見的”。 我個人使用這種技術的所有時間:通常我發現嘲諷涉及的幾行其中“設置場景” ......隨后調用,然后將你所上演的模擬情境“中扮演了”現場的方法.. . . 當您設置布景和道具時,您最不希望演員進入舞台左側並開始表演他們的心...

但這遠遠超出了我的工資等級......我請任何路過的 Mockito 大祭司解釋......

*“通用參數”是正確的術語嗎?

另一種可能導致 spies 出現問題的情況是,當您測試spring bean (使用 spring 測試框架)或其他一些在 test 期間代理對象的框架時

例子

@Autowired
private MonitoringDocumentsRepository repository

void test(){
    repository = Mockito.spy(repository)
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

在上面的代碼中,Spring 和 Mockito 都會嘗試代理您的 MonitoringDocumentsRepository 對象,但 Spring 將是第一個,這將導致真正調用 findMonitoringDocuments 方法。 如果我們在將間諜放在存儲庫對象上之后立即調試我們的代碼,它將在調試器中看起來像這樣:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean 來救援

如果我們使用@SpyBean批注代替@Autowired批注,我們將解決上述問題,SpyBean 批注也將注入存儲庫對象,但它將首先由 Mockito 代理,並且在調試器中看起來像這樣

repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$

這是代碼:

@SpyBean
private MonitoringDocumentsRepository repository

void test(){
    Mockito.doReturn(docs1, docs2)
            .when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}

我發現 spy 調用原始方法的另一個原因。

有人想模擬final堂課,並發現了MockMaker

由於這與我們當前的機制不同,並且這個機制有不同的限制,並且我們想要收集經驗和用戶反饋,因此必須明確激活此功能才能使用; 它可以通過 mockito 擴展機制通過創建文件src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker其中包含一行: mock-maker-inline

來源: https : //github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

在我合並並將該文件帶到我的機器后,我的測試失敗了。

我只需要刪除該行(或文件),然后spy()工作了。

確保類中的方法不被調用的一種方法是使用虛擬方法覆蓋該方法。

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
            @Override
            public void select(TreeItem i) {
                log.debug("SELECT");
            };
        });

給scala用戶的答案:即使將doReturn放在首位也不起作用! 看到這篇文章

正如一些評論中提到的,我的方法是“靜態的”(盡管被類的實例調用)

public class A {
  static void myMethod() {...}
}
A instance = spy(new A());
verify(instance).myMethod(); // still calls the original method because it's static

解決方法是創建一個實例方法或使用一些配置將 Mockito 升級到更新版本: https ://stackoverflow.com/a/62860455/32453

監視真實物體的重要問題

當使用 spies 存根方法時,請使用doReturn()系列方法。

when(Object)將導致調用可能引發異常的實際方法。

List spy = spy(new LinkedList());

//Incorrect , spy.get() will throw IndexOutOfBoundsException   
 when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing    
doReturn("foo").when(spy).get(0);

聚會有點晚,但以上解決方案對我不起作用,所以分享我的 0.02$

Mokcito 版本:1.10.19

我的類

private int handleAction(List<String> argList, String action)

測試.java

MyClass spy = PowerMockito.spy(new MyClass());

以下對我不起作用(正在調用實際方法):

1.

doReturn(0).when(spy , "handleAction", ListUtils.EMPTY_LIST, new String());

2.

doReturn(0).when(spy , "handleAction", any(), anyString());

3.

doReturn(0).when(spy , "handleAction", null, null);

以下工作:

doReturn(0).when(spy , "handleAction", any(List.class), anyString());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM