How to use ArgumentCaptor for stubbing?

前端 未结 3 893
礼貌的吻别
礼貌的吻别 2020-11-27 11:51

In Mockito documentation and javadocs it says

It is recommended to use ArgumentCaptor with verification but not with stubbing.

相关标签:
3条回答
  • 2020-11-27 12:15

    The line

    when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true);
    

    would do the same as

    when(someObject.doSomething(Matchers.any())).thenReturn(true);
    

    So, using argumentCaptor.capture() when stubbing has no added value. Using Matchers.any() shows better what really happens and therefor is better for readability. With argumentCaptor.capture(), you can't read what arguments are really matched. And instead of using any(), you can use more specific matchers when you have more information (class of the expected argument), to improve your test.

    And another problem: If using argumentCaptor.capture() when stubbing it becomes unclear how many values you should expect to be captured after verification. We want to capture a value during verification, not during stubbing because at that point there is no value to capture yet. So what does the argument captors capture method capture during stubbing? It capture anything because there is nothing to be captured yet. I consider it to be undefined behavior and I don't want to use undefined behavior.

    0 讨论(0)
  • 2020-11-27 12:23

    Assuming the following method to test:

    public boolean doSomething(SomeClass arg);
    

    Mockito documentation says that you should not use captor in this way:

    when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true);
    assertThat(argumentCaptor.getValue(), equalTo(expected));
    

    Because you can just use matcher during stubbing:

    when(someObject.doSomething(eq(expected))).thenReturn(true);
    

    But verification is a different story. If your test needs to ensure that this method was called with a specific argument, use ArgumentCaptor and this is the case for which it is designed:

    ArgumentCaptor<SomeClass> argumentCaptor = ArgumentCaptor.forClass(SomeClass.class);
    verify(someObject).doSomething(argumentCaptor.capture());
    assertThat(argumentCaptor.getValue(), equalTo(expected));
    
    0 讨论(0)
  • 2020-11-27 12:29

    Hypothetically, if search landed you on this question then you probably want this:

    doReturn(someReturn).when(someObject).doSomething(argThat(argument -> argument.getName().equals("Bob")));
    

    Why? Because like me you value time and you are not going to implement .equals just for the sake of the single test scenario.

    And 99 % of tests fall apart with null returned from Mock and in a reasonable design you would avoid return null at all costs, use Optional or move to Kotlin. This implies that verify does not need to be used that often and ArgumentCaptors are just too tedious to write.

    0 讨论(0)
提交回复
热议问题