Mockito:Java - 未完成的存根检测

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37747535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 02:45:27  来源:igfitidea点击:

Mockito:Java - Unfinished stubbing detection

javaunit-testingmockingmockitojunit4

提问by L1ghtk3ira

I have been working with Mockito and have run into this error message:

我一直在使用 Mockito 并遇到此错误消息:

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at       com.rbc.rewards.catalogue.service.B2SServicesTest.getProductList_Success(B2SServ icesTest.java:52)

E.g. thenReturn() may be missing.
Examples of correct stubbing:
  when(mock.isOk()).thenReturn(true);
  when(mock.isOk()).thenThrow(exception);
  doThrow(exception).when(mock).someVoidMethod();
Hints:
  1. missing thenReturn()
  2. you are trying to stub a final method, you naughty developer!
  3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed

I have read in two posts on Stack Overflow about this issue but they do not go into full detail. I believe it has something to do with nesting mocks inside a mocking(from what I read). However I do not see or fully understand by the small snippets people have posted.

我在 Stack Overflow 上读过两篇关于这个问题的帖子,但没有详细介绍。我相信这与在模拟中嵌套模拟有关(根据我的阅读)。但是,我没有看到或完全理解人们发布的小片段。

My test class is as followed (leaving out unnecessary code):

我的测试类如下(省略了不必要的代码):

// Mock:  uses Java Reflection in order to create mock objects of each class
@Mock
private Scrapes scrapeS;
@Mock
private SsoS ssoS;
@Mock
private BScrape bScrape;

//@InjectMocks annotation is used to create and inject the mock object
@Mock
private BService bService;


@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

// Testing:
@Test
public void getProductList_Success() throws Exception{

        when(BService.getProductList("cookie", "6753"))
                .thenReturn(
                scrapeS.scrapePost(new String(),
                        new String(),
                        new HashMap<>(),
                        new bService()));
}

Method I need to call:

我需要调用的方法:

public List<prodItem> getProdList(String raw_cookie, String catId) throws Exception {
    String url = ssoSetting.getUrls().BProductList();
    String post = "url" + catId +"url";
    BScrape scraper = new BScrape ();
    Map<String, String> postRequest = new HashMap();
    postRequest.put("searchRequestParams", post);
    return scrapeS.scrapePost(url, raw_cookie, postRequest, scraper);
}

I am also using resources from TutorialsPoint.

我也在使用来自TutorialsPoint 的资源。

It is happening within the @test method and I believe this mocking inside mocking is because I am using it wrong I am assuming.

它发生在@test 方法中,我相信这种在模拟中的模拟是因为我假设我使用它是错误的。

After Implementation from Answer (working):

从答案实施后(工作):

@Mock
private SSetting sSetting = new SSetting ();
// Mock:  uses Java Reflection in order to create mock objects and is injected into InjectMocks
@Mock
private ProdItem productItem = new ProdItem ();
@Mock
private sService scrapeService = new sService ();
@Mock
private BScrape bScrape ;

//@InjectMocks annotation is used to create and inject the mock object
@InjectMocks
private BService bService = new BService ();

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

// Testing:
@Test
public void getProductList_Success() throws Exception{

    List<ProductItem> retList = new ArrayList<>();
    retList.add(new ProductItem());
    retList.add(new ProductItem());

    //try{
    when(b2SServices.getProductList("cookie", "6753")).thenCallRealMethod();

    //test the add functionality
    when(BService .getProdList("cookie", "6753")).thenReturn(retList);


    }catch(Exception exception){
        System.out.println(exception);
    }
}

回答by user1121883

You are in the third case:

您处于第三种情况:

3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed

3:如果完成,您正在'thenReturn'指令之前存根内部另一个模拟的行为

This happens because thenReturn calls method from another mock scrapeS.scrapePostIt is hard to say exactly how to fix this because I would need more implementation details but try building the return object before whenand it shouldn't be the return from another mock. There is a good explanation here: https://stackoverflow.com/a/26319364/1121883

发生这种情况是因为 thenReturn 从另一个模拟调用方法scrapeS.scrapePost很难确切地说如何解决这个问题,因为我需要更多的实现细节,但尝试之前构建返回对象when,它不应该是另一个模拟的返回。这里有一个很好的解释:https: //stackoverflow.com/a/26319364/1121883

Here is a small example. You code is similar to playtest. In then return you should provide an object instead of the call on the mock like in fixtest.

这是一个小例子。您的代码类似于play测试。然后返回时,您应该提供一个对象,而不是像fix测试中那样在模拟上调用。

@Test
public void play(){
    A a = mock(A.class);
    B b = mock(B.class);
    when(a.a("input")).thenReturn(b.b("input"));
}

@Test
public void fix(){
    A a = mock(A.class);
    B b = mock(B.class);
    String returnString = "b";
    when(a.a("input")).thenReturn(returnString);
}

static class A{
    String a(String in){
        return "a";
    }
} 

static class B{
    String b(String in){
        return "b";
    }
}

Your code should be something like:

你的代码应该是这样的:

    List<prodItem> retList = new ArrayList<>();
    retList.add(new ProdItem());
    retList.add(new ProdItem());
    when(bService.getProductList("cookie", "6753")).thenReturn(retList);

回答by SpaceTrucker

The (a bit simplified) evaluation order of your code

代码的(有点简化)评估顺序

when(bService.getProductList("cookie", "6753"))
                .thenReturn(
                scrapeS.scrapePost(new String(),
                        new String(),
                        new HashMap<>(),
                        new bService()));

is first:

首先是:

bService.getProductList("cookie", "6753")

second

第二

when(/*...*/)

third

第三

scrapeS.scrapePost(new String(),
                        new String(),
                        new HashMap<>(),
                        new bService())

.

.

So while trying to mock bServiceyou use mock scrapeS.

因此,在尝试模拟时,bService您可以使用 mock scrapeS

Note that this doesn't make sense at all. Basically you are currently trying to give bService.getProductListan implementation which should use scrapeS. But bServiceis a mock and therefore has no implementaion.

请注意,这完全没有意义。基本上,您目前正在尝试提供bService.getProductList一个应该使用scrapeS. 但是bService是一个模拟,因此没有实现。

If you want that the invocations of bServiceand scrapeSreturn the same object then store that object into a local variable and use that local variable in the thenReturnclause of both methods.

如果您希望调用bServicescrapeS返回同一对象,则将该对象存储到局部变量中,并在thenReturn两种方法的子句中使用该局部变量。

Object returnValue = /*whatever the return value is*/
when(bService.getProductList("cookie", "6753")).thenReturn(returnValue);
when(scrapeS.scrapePost(new String(), new String(), new HashMap<>(), new bService())).thenReturn(returnValue);