I have following code I want to test:
public class MessageService { private MessageDAO dao; public void acceptFromOffice(Message message) { message.setStatus(0); dao.makePersistent(message); message.setStatus(1); dao.makePersistent(message); } public void setDao (MessageDAO mD) { this.dao = mD; } } public class Message { private int status; public int getStatus () { return status; } public void setStatus (int s) { this.status = s; } public boolean equals (Object o) { return status == ((Message) o).status; } public int hashCode () { return status; } }
I need to verify, that method acceptFromOffice really sets status to 0, than persist message, then chage its status to 1, and then persist it again.
With Mockito, I have tried to do following:
@Test public void testAcceptFromOffice () throws Exception { MessageDAO messageDAO = mock(MessageDAO.class); MessageService messageService = new MessageService(); messageService.setDao(messageDAO); final Message message = spy(new Message()); messageService.acceptFromOffice(message); verify(messageDAO).makePersistent(argThat(new BaseMatcher<Message>() { public boolean matches (Object item) { return ((Message) item).getStatus() == 0; } public void describeTo (Description description) { } })); verify(messageDAO).makePersistent(argThat(new BaseMatcher<Message>() { public boolean matches (Object item) { return ((Message) item).getStatus() == 1; } public void describeTo (Description description) { } })); }
I actually expect here that verification will verify calling twice of makePersistent method with a different Message object's state. But it fails saying that
Argument(s) are different!
Any clues?
Whenever we write unit test cases for any method we expect a return value from the method and generally use assert for checking if the functions return the value that we expect it to return, but in the case of void methods, they do not return any value.
However, doNothing() is Mockito's default behavior for void methods. This version of whenAddCalledVerified() accomplishes the same thing as the one above: @Test public void whenAddCalledVerified() { MyList myList = mock(MyList.class); myList.add(0, ""); verify(myList, times(1)).add(0, ""); }
For Mockito, there is no direct support to mock private and static methods. In order to test private methods, you will need to refactor the code to change the access to protected (or package) and you will have to avoid static/final methods.
Testing your code is not trivial, though not impossible. My first idea was to use ArgumentCaptor, which is much easier both to use and comprehend compared to ArgumentMatcher. Unfortunately the test still fails - reasons are certainly beyond the scope of this answer, but I may help if that interests you. Still I find this test case interesting enough to be shown (not correct solution):
@RunWith(MockitoJUnitRunner.class) public class MessageServiceTest { @Mock private MessageDAO messageDAO = mock(MessageDAO.class); private MessageService messageService = new MessageService(); @Before public void setup() { messageService.setDao(messageDAO); } @Test public void testAcceptFromOffice() throws Exception { //given final Message message = new Message(); //when messageService.acceptFromOffice(message); //then ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class); verify(messageDAO, times(2)).makePersistent(captor.capture()); final List<Message> params = captor.getAllValues(); assertThat(params).containsExactly(message, message); assertThat(params.get(0).getStatus()).isEqualTo(0); assertThat(params.get(1).getStatus()).isEqualTo(1); } }
Unfortunately the working solution requires somewhat complicated use of Answer. In a nutshell, instead of letting Mockito to record and verify each invocation, you are providing sort of callback method that is executed every time your test code executes given mock. In this callback method (MakePersistentCallback
object in our example) you have access both to parameters and you can alter return value. This is a heavy cannon and you should use it with care:
@Test public void testAcceptFromOffice2() throws Exception { //given final Message message = new Message(); doAnswer(new MakePersistentCallback()).when(messageDAO).makePersistent(message); //when messageService.acceptFromOffice(message); //then verify(messageDAO, times(2)).makePersistent(message); } private static class MakePersistentCallback implements Answer { private int[] expectedStatuses = {0, 1}; private int invocationNo; @Override public Object answer(InvocationOnMock invocation) throws Throwable { final Message actual = (Message)invocation.getArguments()[0]; assertThat(actual.getStatus()).isEqualTo(expectedStatuses[invocationNo++]); return null; } }
The example is not complete, but now the test succeeds and, more importantly, fails when you change almost anything in CUT. As you can see MakePersistentCallback.answer
method is called every time mocked messageService.acceptFromOffice(message)
is called. Inside naswer
you can perform all the verifications you want.
NB: Use with caution, maintaining such tests can be troublesome to say the least.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With