我想测试一个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这是一篇非常好的文章,走的是完全相同的路线,但又将其与嘲讽和基于交互的测试区分开来。
你做错了。首先,您应该使用BDD或AAA关键字和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");
}
}
在上面的例子中,我也推断了日志事件的内容,但这只是为了给出可能的想法。