BDDMockito 风格

行为驱动开发(BDD)测试风格围绕测试中的给定何时然后阶段。然而,经典的 Mockito 将 when 单词用于给定阶段,并且不包括可以包含 BDD 的其他自然语言结构。因此,在 1.8.0 版本中引入了 BDDMockito 别名,以便于行为驱动的测试。

最常见的情况是存根方法的返回。在下面的示例中,如果使用等于 givenName 的参数调用,则模拟 StudentRepositorygetStudent(String) 方法将返回 new Student(givenName, givenScore)

import static org.mockito.BDDMockito.*;

public class ScoreServiceTest {

    private StudentRepository studentRepository = mock(StudentRepository.class);

    private ScoreService objectUnderTest = new ScoreService(studentRepository);

    @Test
    public void shouldCalculateAndReturnScore() throws Exception {
        //given
        String givenName = "Johnny";
        int givenScore = 10;
        given(studentRepository.getStudent(givenName))
            .willReturn(new Student(givenName, givenScore));

        //when
        String actualScore = objectUnderTest.calculateStudentScore(givenName);

        //then
        assertEquals(givenScore, actualScore);
    }
}

有时需要检查从依赖项抛出的异常是否在被测试的方法中正确处理或重新抛出。这样的行为可以通过这种方式在给定阶段中存根:

willThrow(new RuntimeException())).given(mock).getData();

有时需要设置一些存根方法应该引入的副作用。特别是它可以派上用场:

  • stubbed 方法是一种应该改变传递对象的内部状态的方法

  • stubbed 方法是一种 void 方法

这种行为可以在给定阶段以答案存根:

willAnswer(invocation -> this.prepareData(invocation.getArguments()[0])).given(mock).processData();

当需要验证与模拟的交互时,可以使用 should()should(VerificationMode)(仅自 1.10.5)方法在 then 阶段完成:

then(mock).should().getData(); // verifies that getData() was called once
then(mock).should(times(2)).processData(); // verifies that processData() was called twice

当需要验证除了已经验证之外没有更多与模拟的交互时,可以使用 shouldHaveNoMoreInteractions()(从 2.0.0 开始)在 then 阶段完成:

then(mock).shouldHaveNoMoreInteractions(); // analogue of verifyNoMoreInteractions(mock) in classical Mockito

当需要验证绝对没有与 mock 的交互时,可以使用 shouldHaveNoMoreInteractions()(从 2.0.0 开始)在 then 阶段完成:

then(mock).shouldHaveZeroInteractions(); // analogue of verifyZeroInteractions(mock) in classical Mockito

当需要检查方法是否被调用时 ,可以在 then 阶段使用 should(InOrder)(自 1.10.5 起)和 should(InOrder, VerificationMode)(自 2.0.0 起)完成:

InOrder inOrder = inOrder(mock);

// test body here

then(mock).should(inOrder).getData(); // the first invocation on the mock should be getData() invocation
then(mock).should(inOrder, times(2)).processData(); // the second and third invocations on the mock should be processData() invocations