1

I am trying to use argument capture to determine what arguments are being passed to a mocked Mockito method, but I am not able to capture any values.

class CombinedEvent 
{
   final List<String> events;

   public CombinedEvent() {
      this.events = new ArrayList<>();
      this.events.add("WATCHITM");
      this.events.add("BIDITEM");
   }
}

Holder class

class CombinedNotificationAdapter {
    private CombinedEvent combinedEvent;

     CombinedNotificationAdapter() {
        this.combinedEvent  = new CombinedEvent();
     }

     public boolean isEnabled(String user, NotificationPreferenceManager preferenceManager) {
         boolean status = true;
         for (String event : combinedEvent.events) {
            status = status && preferenceManager.isEventEnabled(user, event);
         }
         return status;

     }
}

My unit test

@RunWith(JUnit4.class)
class CombinedNotificationAdapterTest {
   private CombinedNotificationAdapter adapter;

   @Mock
   private NotificationPreferenceManager preferenceManager;

   @Before
   public void setUp() {
       MockitoAnnotations.initMocks(this);
       adapter = new CombinedNotificationAdapter();
   }

   @Test
   public void testIsEnabled() {
      doReturn(true).when(preferenceManager).isEventEnabled(eq("test"), anyString());
      Assert.assertTrue(adapter.isEnabled("test", preferenceManager));
      ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
       verify(preferenceManager, times(2)).isEventEnabled(eq("test"), captor.capture());
       System.out.println(captor.getAllValues());
   }
}

The output of captor.getAllValues() is an empty list. I would like the values to return a list of WATCHITM and BIDITEM. I don't know what I am going wrong.

Reference:

  1. https://static.javadoc.io/org.mockito/mockito-core/2.28.2/org/mockito/Mockito.html#15

  2. https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/ArgumentCaptor.html

Krish
  • 63
  • 6
  • 1
    You are capturing `preferenceManager.isEventEnabled(String, String)`, but in the main code you call `preferenceManager.isEventEnabled(event)` – Hari Menon Jun 09 '19 at 05:52
  • @HariMenon See this line: `Assert.assertTrue(adapter.isEnabled("test", preferenceManager)); ` I am passing both "test" and preferenceManager as argument. – Krish Jun 09 '19 at 05:58
  • @Krish But Hari is pointing out that `adapter.isEnabled` does not call a two-argument `isEventEnabled` method, it calls a one-argument version. So either you're giving us an invalid or incomplete example, or he's pointing to a legitimate problem with your test, where you're trying to capture arguments for a method that is never called. – Mark Peters Jun 09 '19 at 06:03
  • @MarkPeters I pasted the wrong code. It is fixed now. – Krish Jun 09 '19 at 06:07
  • Unrelated: don't use doReturn. Prefer to use when().thenReturn()! – GhostCat Jun 09 '19 at 06:26

1 Answers1

0

I think you are overdoing:

doReturn(true)
 . when(preferenceManager)
 .isEventEnabled(eq("test"), anyString()):

You are scrubbing that expected method invocation and then combining that with your argument captor. And that does not work. You can either stub or capture, not both things! See this existing question for example.

My suggestion: look at this answer and learn how to create your own Answer object. Those get passed an instance of InvocationOnMock. And that class allows you to check the arguments passed into the mocked calls, too!

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • This is a simplified test case. The list I am iterating through is fetched by a service call. I don't know what those values are. That is why I want to capture them. If I were to use an answer then, I would need to know what values are being used in order to match them. That is why I used `anyString` as matcher. – Krish Jun 09 '19 at 06:49
  • @Krish Then you are not writing a unit test. And make no mistake: I answered the question you asked. You shouldnt come back and go "but see, my real problem is something different". You asked how to get your example to work, and I think I gave you good guidance to solve that problem. When you now have a different problem, please consider accepting the answer here and maybe asking a new different question. – GhostCat Jun 09 '19 at 07:57
  • @Krish But as said, when your "real" arguments are coming from some service, what exactly is the purpose of your unit test then? First of all, unit tests work in isolation. They should **not** rely on external things such as a service, database, ... so, as said: it is really not clear why you would write a "unit" test that does **not** know what values come in. – GhostCat Jun 09 '19 at 08:00