Mockito测试void方法

gla*_*666 19 java unit-testing mockito

我有以下要测试的代码:

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; }
}
Run Code Online (Sandbox Code Playgroud)

我需要验证,该方法acceptFromOffice确实将状态设置为0,而不是持久消息,然后将其状态设置为1,然后再次保留它.

有了Mockito,我试图做以下事情:

@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) { }
        }));

    }
Run Code Online (Sandbox Code Playgroud)

我实际上在这里期望验证将验证使用不同Message对象的状态调用两次makePersistent方法.但它没有说出来

争论是不同的!

有线索吗?

Tom*_*icz 25

测试代码并非易事,但并非不可能.我的第一个想法是使用ArgumentCaptor,与ArgumentMatcher相比,它更易于使用和理解.不幸的是,测试仍然失败 - 原因肯定超出了这个答案的范围,但如果您感兴趣,我可以提供帮助.我仍然发现这个测试用例足够有趣,可以显示(不正确的解决方案):

@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);
    }

}
Run Code Online (Sandbox Code Playgroud)

不幸的是,工作解决方案需要使用一些复杂的Answer.简而言之,您不是让Mockito记录并验证每次调用,而是提供一种回调方法,每次执行测试代码时都会执行mock.在这个回调方法(MakePersistentCallback我们示例中的对象)中,您可以访问参数,并且可以更改返回值.这是一个重型大炮,你应该小心使用它:

    @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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

该示例并不完整,但现在测试成功,更重要的是,当您在CUT中更改几乎任何内容时都会失败.正如您所见,MakePersistentCallback.answer每次messageService.acceptFromOffice(message)调用mock时都会调用方法.在里面,naswer您可以执行所需的所有验证.

注意:谨慎使用,维持此类测试至少可以说是麻烦.