嘲笑抛出异常的服务?

时间:2012-02-23 09:31:18

标签: java unit-testing junit mockito

我想测试委托给服务方法“isInProgress”的JSF Backing-Bean方法“isInProgress”。当服务方法抛出异常时,bean应该在特定事件记录器上放置一个事件并返回false。

当我调试以下测试时,我进入了catch块。模拟服务不会抛出异常,但会返回“默认答案”,对于布尔值,它是false。 我做错了什么?

我也想知道是否可以以某种方式避免围绕“when”调用的try-catch,因为被测试的bean吞下了实际的异常。事实上,我认为“声明性地”将方法的名称传递给“when”就足够了。 有没有办法让它更清洁?

@Test
public void testIsInProgressExeption() {
    //prepare object and inputs
    MyBean bean = new MyBean();
    MyService service = mock(MyAdapterService.class);
    bean.setService(service);

    try {
        when(bean.getService().isInProgress()).thenThrow(new Exception());
    } catch (Exception e) {

        //prepare expected object and result
        MyBean expectedBean = new MyBean();
        expectedBean.setService(service);
        boolean expected = false;

        //execute method under test
        boolean actual = bean.isInProgress();

        //check return values and exceptions
        assertEquals(expected, actual);

        //check that bean did not change unexpectedly
        assertTrue(bean.equals(expectedBean));

        //check sideeffects on event log
        assertTrue(logEvents.containsMessage("MDI09"));
    }

}

此处参考更新的测试:

@Test
public void testIsInProgressExeption() throws Exception {
    //prepare object and inputs
    MyBean bean = new MyBean();
    MyService service = mock(MyAdapterService.class);
    bean.setService(service);

    when(bean.getService().isInProgress()).thenThrow(new Exception());

    //prepare expected object and result
    MyBean expectedBean = new MyBean();
    expectedBean.setService(service);
    boolean expected = false;

    //execute method under test
    boolean actual = bean.isInProgress();

    //check return values and exceptions
    assertEquals(expected, actual);

    //check that bean did not change unexpectedly
    assertTrue(bean.equals(expectedBean));

    //check sideeffects on event log
    assertTrue(logEvents.containsMessage("MDI09"));

}

3 个答案:

答案 0 :(得分:3)

将when子句移出try块并将其更改为:

when(service.isInProgress()).thenThrow(new Exception());

现在它应该在调用时抛出异常。

答案 1 :(得分:0)

对于记录,我正在进行状态测试。有趣的是,Fowler在http://martinfowler.com/articles/mocksArentStubs.html中发布了一篇非常好的文章,该文章的路线完全相同,但后来将其与基于模拟和交互的测试区分开来。

答案 2 :(得分:-1)

你做错了。首先,您应该使用BDD BDD AAA 关键字进行测试:

@Test public void testIsInProgressExeption() {
    // given

    // when

    // then

}

给出 部分,您将编写灯具,即测试场景的设置。在 部分,您将调用生产代码,即测试主题。最后,在 部分,您将编写验证和/或断言。

存根进入固定装置,因此这条线错位,它不属于这里,它只是行为的定义。

when(bean.getService().isInProgress()).thenThrow(new Exception());

但是,您应该直接使用服务引用而不是bean.getService(),这是很难的。

我真的不明白你为什么要在catch子句中创建bean的新实例,这很奇怪。但这是我写下测试的方式。请注意我在单元测试名称中解释测试的实际测试行为,在camel情况下写这个是读取痛苦的方法,所以我使用强调约定,它没关系在测试中

@Test public void when_service_throw_Exception_InProgress_then_returns_false() throws Exception {
    // given
    MyBean bean = new MyBean();
    MyService service = mock(MyAdapterService.class);
    bean.setService(service);

    when(service.isInProgress()).thenThrow(new Exception());

    // when
    boolean result = bean.isInProgress();

    // then
    assertFalse(result);
}

此外,我会在事件上拆分断言,这是一种不同的行为:

@Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
    // given
    MyBean bean = new MyBean();
    MyService service = mock(MyAdapterService.class);
    bean.setService(service);
    // somehow set up the logEvents collaborator

    when(service.isInProgress()).thenThrow(new Exception());

    // when
    bean.isInProgress();

    // then
    assertTrue(logEvents.containsMessage("MDI09"));
}

你甚至可以进一步简化夹具,如果你使用JUnit,你可以编写这段代码:

@RunWith(MockitoJUnitRunner.class)
public class MyBeanTest {
    @Mock MyService service;
    @Mock LogEvents logEvents;
    @InjectMocks MyBean bean;


    @Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
        // given
        when(service.isInProgress()).thenThrow(Exception.class);

        // when
        bean.isInProgress();

        // then
        verify(logEvents).logEvent("MDI09");
    }
}

在上面的示例中,我还推断了日志事件的内容,但这只是为了说明可能的事情。