单元测试显而易见
Unit testing the obvious
在尝试为 class 编写测试用例时,其功能更多地涉及样板代码而不是业务逻辑。我开始怀疑单元测试是否真的值得 class。但话又说回来,在使用 TDD 时,建议我们为我们添加的任何逻辑块编写测试。
例如下面的class,只是使用DI注入依赖项并获取配置参数来设置应用程序的运行。除了单元测试是否正确注入了依赖关系,或者如果在 运行 完成时调用了销毁(这将是对 java CDI 框架的单元测试而不是我自己的代码),我还能单元测试什么?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
@Singleton
@Startup
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class PipsAlprConnectionRunner {
@Inject
private PipsAlprConfiguration config;
@Inject
private PipsAlprConnector connector;
@Inject
private Scheduler scheduler;
@Inject
@PipsAlprAdapterService
private ServiceStatus status;
private Timer connectorTimer = null;
@PostConstruct
public void initialized() {
status.started();
connectorTimer = scheduler.schedule(connector, 0,
1000 * config.getPollPeriodSeconds());
status.operational();
}
@PreDestroy
public void destroy() {
connectorTimer.cancel();
connector.shutdown();
status.stopped();
}
}
在上面class,我想不出任何利用TDD的测试场景,所以就想出了代码,现在我想知道我到底可以在这里单元测试什么。
好吧,可以证明 class 做了一些事情。它改变一个状态,它启动一个定时器。您可以通过 Mockito 将这些对象的模拟注入到 class 中,然后确保 initialized()
和 destroy()
都执行您期望它们对这些模拟执行的操作。
@RunWith(MockitoJUnitRunner.class)
public class PipsAlprConnectionRunner {
@Mock
private PipsAlprConfiguration config;
@Mock
private PipsAlprConnector connector;
@Mock
private Scheduler scheduler;
@Mock
private ServiceStatus status;
@InjectMocks
private PipsAlprConnectionRunner pipsAlprConnectionRunner ;
@Test
public void initialized_should_set_status_started() {
pipsAlprConnectionRunner.initialized();
Mockito.verify(status).started();
}
// etc.
}
如果您想为每个 "point of failure" 创建一个方法或为每个 method/test 创建一个方法,这几乎是个人品味的问题。
就我个人而言,我会说目标是 100% 的覆盖率,因此即使是非常简单的 class 主要是委托的内容也应该被覆盖。如果有人改变了什么会发生什么?测试确保此类更改不会破坏现有功能。
在尝试为 class 编写测试用例时,其功能更多地涉及样板代码而不是业务逻辑。我开始怀疑单元测试是否真的值得 class。但话又说回来,在使用 TDD 时,建议我们为我们添加的任何逻辑块编写测试。
例如下面的class,只是使用DI注入依赖项并获取配置参数来设置应用程序的运行。除了单元测试是否正确注入了依赖关系,或者如果在 运行 完成时调用了销毁(这将是对 java CDI 框架的单元测试而不是我自己的代码),我还能单元测试什么?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
@Singleton
@Startup
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class PipsAlprConnectionRunner {
@Inject
private PipsAlprConfiguration config;
@Inject
private PipsAlprConnector connector;
@Inject
private Scheduler scheduler;
@Inject
@PipsAlprAdapterService
private ServiceStatus status;
private Timer connectorTimer = null;
@PostConstruct
public void initialized() {
status.started();
connectorTimer = scheduler.schedule(connector, 0,
1000 * config.getPollPeriodSeconds());
status.operational();
}
@PreDestroy
public void destroy() {
connectorTimer.cancel();
connector.shutdown();
status.stopped();
}
}
在上面class,我想不出任何利用TDD的测试场景,所以就想出了代码,现在我想知道我到底可以在这里单元测试什么。
好吧,可以证明 class 做了一些事情。它改变一个状态,它启动一个定时器。您可以通过 Mockito 将这些对象的模拟注入到 class 中,然后确保 initialized()
和 destroy()
都执行您期望它们对这些模拟执行的操作。
@RunWith(MockitoJUnitRunner.class)
public class PipsAlprConnectionRunner {
@Mock
private PipsAlprConfiguration config;
@Mock
private PipsAlprConnector connector;
@Mock
private Scheduler scheduler;
@Mock
private ServiceStatus status;
@InjectMocks
private PipsAlprConnectionRunner pipsAlprConnectionRunner ;
@Test
public void initialized_should_set_status_started() {
pipsAlprConnectionRunner.initialized();
Mockito.verify(status).started();
}
// etc.
}
如果您想为每个 "point of failure" 创建一个方法或为每个 method/test 创建一个方法,这几乎是个人品味的问题。
就我个人而言,我会说目标是 100% 的覆盖率,因此即使是非常简单的 class 主要是委托的内容也应该被覆盖。如果有人改变了什么会发生什么?测试确保此类更改不会破坏现有功能。