我想澄清TDD范围内的重构。
以前:
class Somclass{
public void sendMessage(){
WebServiceStub stub = new WebServiceStub();
...
stub.sendMsg();
}
}
后:
class Somclass{
private WebServiceStub stub;
public void sendMessage(){
...
if(stub == null){
stub = new WebServiceStub();
}
...
stub.sendMsg();
}
}
所以我想验证 sendMsg() 方法并用结果做出一些断言。为了有可能模拟这个存根,我将这个存根局部变量移动到实例变量。这样我就可以将模拟存根设置为类并在测试类中进行验证和断言。例如:
@Test
public void testSMth(){
wsProvider.setStub(stubMock);
verify(stubMock).sendMsg();
...asserts
}
这种方法不是线程安全的,我应该做一些并发修改。此修改可能会导致错误。所以在局部变量中,线程是安全的。
我还可以创建将返回WebServiceStub实例的工厂。但这种方法会产生新的类,因为这种情况很常见。
有一个问题:如何测试这种情况,是否测试可能导致错误的成本修改?
你的类应该有WebService
对象(我拒绝称它为"存根")作为字段。
class Someclass{
@Resource
private WebService ws;
public void sendMessage(){
ws.sendMsg();
}
}
它应该注入您选择的 DI 框架。在测试中,您可以将其设置为模拟。不需要懒惰的getter,正如您所指出的那样,这不是线程安全的。
使用构造函数注入来避免未设置依赖项的可能性。这将允许您在测试中轻松使用模拟。
如果 WebServiceStub
类实际上不是线程安全的(但如果 WebServiceStub
是由 JAX-WS 生成的,那么您应该知道 metro/jax-ws 存根通常是线程安全的),那么是的,您将不得不使用工厂。这没什么大不了的,它不应该减慢你的速度。如果需要,可以使用静态内部类。
它看起来几乎是正确的,但如果存根 == Null,则从不实例化存根。相反,thow ArgumentNullException。Null 永远不应该被接受为一个有效的参数(除非你有一个非常、非常、非常好的理由)。