我正在编写端点单元测试,对于大多数人来说,有一个应该被模拟的外部Web服务,或者其中几个。
起初,我在测试中创建了模拟,当端点测试只使用一个外部服务时,模拟创建基本上就是一个线程。
随着用例变得越来越复杂,我需要为单个端点测试模拟几个服务和异常。 我已将这些模拟创建放在工厂后面,这些工厂都扩展了单个工厂并使用了构建器模式。
在该基础工厂中,有一个内部类,我用它作为MockWebServiceServer
的构建器。
protected class MultiStepMockBuilder {
private List<Object> mockActions = new ArrayList<Object>();
private WebServiceGatewaySupport gatewaySupport;
protected MultiStepMockBuilder(WebServiceGatewaySupport gatewaySupport) {
this.gatewaySupport = gatewaySupport;
}
protected MultiStepMockBuilder exception(RuntimeException exception) {
mockActions.add(exception);
return this;
}
protected MultiStepMockBuilder resource(Resource resource) {
mockActions.add(resource);
return this;
}
protected MockWebServiceServer build() {
MockWebServiceServer server = MockWebServiceServer.createServer(gatewaySupport);
for(Object mock: mockActions) {
if (mock instanceof RuntimeException) {
server.expect(anything()).andRespond(withException((RuntimeException)mock));
}
else if (mock instanceof Resource)
{
try
{
server.expect(anything()).andRespond(withSoapEnvelope((Resource) mock));
} catch (IOException e) {e.printStackTrace();}
}
else
throw new RuntimeException("unusuported mock action");
}
return server;
}
}
}
所以我现在可以做这样的事情来创建mock:
return new MultiStepMockBuilder(gatewaySupport).resource(success).exception(new WebServiceIOException("reserve timeout"))
.resource(invalidMsisdn)
.build();
我对此实现的问题是依赖instanceof
运算符,我从未在equals
之外使用。
在这种情况下,是否有另一种方法可以实现instanceof运算符?关于instanceof
主题的问题,每个人都认为它应该只在equals
内使用,因此我觉得这很脏[&1;}溶液
是否有instanceof
运算符的替代,在Spring中或作为不同的设计,同时保持fluent interface
创建模拟?
答案 0 :(得分:1)
我不太清楚Spring对这一特定区域的具体评论,但对我而言,这似乎只是一种设计。通常,当您面临使用instanceof
时,这意味着您需要知道类型,但您不具备此类型。通常情况下,我们可能需要重构,以实现更具凝聚力的设计,避免这种问题。
类型信息丢失的根源在模拟操作的List
中,当前只是存储为List
Object
的{{1}}。一种帮助解决此问题的方法是查看List
的类型,并考虑是否有更好的类型可以存储在List
中,以后可能会对我们有所帮助。所以我们最终可能会得到像这样的重构。
private List<MockAction> mockActions = new ArrayList<MockAction>();
当然,我们必须决定MockAction
实际上是什么,因为我们已经做好了准备。也许是这样的:
interface MockAction {
void performAction(MockWebServiceServer server);
}
所以,我们刚刚创建了这个MockAction
界面,我们已经决定执行操作而不是调用者 - 我们会将服务器传递给它并询问MockAction
执行自己。如果我们这样做,那么就不需要instanceof - 因为特定类型的MockAction
会知道它们包含什么。
那么,我们需要什么类型的MockAction
?
class ExceptionAction implements MockAction {
private final Exception exception;
private ExceptionAction(final Exception exception) {
this.exception = exception;
}
public void performAction(final MockWebServiceServer server) {
server.expect(anything()).andRespond(withException(exception);
}
}
class ResourceAction implements MockAction {
private final Resource resource;
private ResourceAction(final Resource resource) {
this.resource = resource;
}
public void performAction(final MockWebServiceServer server) {
/* I've left out the exception handling */
server.expect(anything()).andRespond(withSoapEnvelope(resource));
}
}
好的,现在我们已经到了这一步,有一些松散的目的。
我们仍然在MockAction
列表中添加例外 - 但我们需要更改添加方法以确保我们在列表中放置正确的内容。这些方法的新版本可能如下所示:
protected MultiStepMockBuilder exception(RuntimeException exception) {
mockActions.add(new ExceptionAction(exception));
return this;
}
protected MultiStepMockBuilder resource(Resource resource) {
mockActions.add(new ResourceAction(resource));
return this;
}
所以,现在我们已经离开了我们的界面,但我们将资源或异常包装起来,因为它们已添加到列表中,以便我们具有以后需要的类型特异性。
然后最后,我们需要重构实际调用的方法,现在看起来像这样 - 这更简单,更清晰。
protected MockWebServiceServer build() {
MockWebServiceServer server = MockWebServiceServer.createServer(gatewaySupport);
for(MockAction action: mockActions) {
action.performAction(server);
}
return server;
}