模拟抛出异常的服务



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

当我调试下面的测试时,我进入了catch块。模拟服务不会抛出异常,但会返回一个"默认答案",对于th布尔值,该答案为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"));
}

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

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

现在,它应该在被调用时抛出一个异常。

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

你做错了。首先,您应该使用BDDAAA关键字和BDD:来布置测试

@Test public void testIsInProgressExeption() {
    // given
    // when
    // then
}

给定的部分中,您将编写夹具,即测试场景的设置。在部分中,您将调用生产代码,即测试对象。最后,在when部分中,您将编写验证和/或断言。

Stubs在fixture中,所以这行放错地方了,它不属于这里,它只是行为的定义。

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

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

我真的不明白为什么要在catch子句中创建一个新的bean实例,这很奇怪。但以下是我编写测试的方法。请注意,我在单元测试名称中解释了测试实际测试的行为,用驼色写这篇文章读起来很痛苦,所以我使用了下划线约定,在测试中也可以

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

您甚至可以进一步简化fixture,如果您使用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");
    }
}

在上面的例子中,我也推断了日志事件的内容,但这只是为了给出可能的想法。

最新更新