在为多个Web服务指定mocks时替代instanceof运算符

时间:2015-08-08 18:23:02

标签: java spring unit-testing instanceof

我正在编写端点单元测试,对于大多数人来说,有一个应该被模拟的外部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创建模拟?

1 个答案:

答案 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;
}