Sebastian Wramba
Sebastian Wramba

Reputation: 10127

Test public method that calls internal methods depending on input

So we have this method here that is accessible by the rest of the system which calls underlying methods according to the input.

public SomeReturnObj doSomethingWithInputs(List<Input> inputs) {
  for(Input input : inputs) {
    if(input.getName().equals("A") {
        handleAInput(input);
    }
    else if(input.getName().equals("B") {
        handleBInput(input);
    }
    else { ... }
  }

  // ...
}

To get a good code coverage, I would like to test, that if I put a list with two Inputs with name A and three with name B, the corresponding internal methods are called twice or three times, respectively.

So I've tried the following:

@Test
public void separatingInputsByName() {
    Input entry1 = mock(Input .class);
    Input entry2 = mock(Input .class);
    Input entry3 = mock(Input .class);

    doReturn("A").when(entry1).getName();
    doReturn("A").when(entry2).getName();
    doReturn("B").when(entry3).getName();

    ClassUnderTest sut = mock(ClassUnderTest .class);

    sut.doSomethingWithInputs(Arrays.asList(entry1, entry2, entry3));

    verify(sut).handleAInput(entry1);
    verify(sut).handleAInput(entry2);
    verify(sut).handleBInput(entry3);
}

Unfortunately this does not lead to a proper invocation of the internal methods, probably because the class under test is mocked, so the method implementation is different.

How can I test/verify a method like this properly?

Upvotes: 0

Views: 288

Answers (1)

fge
fge

Reputation: 121730

You should use spy(), not mock().

When you use mock(), all methods are "overriden" so that default actions are taken instead of calling the real methods; spy() will simply register method invocations.

Therefore:

ClassUnderTest sut = spy(new ClassUnderTest(...));

sut.doSomethingWithInputs(Arrays.asList(entry1, entry2, entry3));

verify(sut).handleAInput(entry1);
verify(sut).handleAInput(entry2);
verify(sut).handleBInput(entry3);
verifyNoMoreInteractions(sut); // if necessary

Also, you can:

when(entry1.getName()).thenReturn("A");

Personally, I find it easier to read, but that's a matter of taste, of course.

Also, you can use InOrder in your case:

final InOrder inOrder = inOrder(sut);

inOrder.verify(sut).handleAInput(entry1);
// etc

Upvotes: 1

Related Questions