吉姆和我在上周一直在做一些工作,其中涉及调用neo4j的HA状态URI来检查实例是否是主/从属,并且我们一直在使用jersey-client 。
该代码大致如下所示:
class Neo4jInstance {private Client httpClient;private URI hostname;public Neo4jInstance(Client httpClient, URI hostname) {this.httpClient = httpClient;this.hostname = hostname;}public Boolean isSlave() {String slaveURI = hostname.toString() + ":7474/db/manage/server/ha/slave";ClientResponse response = httpClient.resource(slaveURI).accept(TEXT_PLAIN).get(ClientResponse.class);return Boolean.parseBoolean(response.getEntity(String.class));}
}
在针对该代码编写一些测试时,我们希望保留对HA从URI的实际调用,以便我们可以模拟这两种情况,并且进行简短搜索表明,使用Mockito是可行的方法 。
我们最终进行了如下所示的测试:
@Test
public void shouldIndicateInstanceIsSlave() {Client client = mock( Client.class );WebResource webResource = mock( WebResource.class );WebResource.Builder builder = mock( WebResource.Builder.class );ClientResponse clientResponse = mock( ClientResponse.class );when( builder.get( ClientResponse.class ) ).thenReturn( clientResponse );when( clientResponse.getEntity( String.class ) ).thenReturn( "true" );when( webResource.accept( anyString() ) ).thenReturn( builder );when( client.resource( anyString() ) ).thenReturn( webResource );Boolean isSlave = new Neo4jInstance(client, URI.create("http://localhost")).isSlave();assertTrue(isSlave);
}
这很粗糙,但是可以完成工作。
我认为应该有更好的方法,所以我继续搜索,最终在邮件列表中发现了此帖子,该帖子建议创建一个自定义ClientHandler并在其中存储请求/响应。
我可以这样做,并用一个仅覆盖我们非常具体的用例的小DSL进行包装:
private static ClientBuilder client() {return new ClientBuilder();
}static class ClientBuilder {private String uri;private int statusCode;private String content;public ClientBuilder requestFor(String uri) {this.uri = uri;return this;}public ClientBuilder returns(int statusCode) {this.statusCode = statusCode;return this;}public Client create() {return new Client() {public ClientResponse handle(ClientRequest request) throws ClientHandlerException {if (request.getURI().toString().equals(uri)) {InBoundHeaders headers = new InBoundHeaders();headers.put("Content-Type", asList("text/plain"));return createDummyResponse(headers);}throw new RuntimeException("No stub defined for " + request.getURI());}};}private ClientResponse createDummyResponse(InBoundHeaders headers) {return new ClientResponse(statusCode, headers, new ByteArrayInputStream(content.getBytes()), messageBodyWorkers());}private MessageBodyWorkers messageBodyWorkers() {return new MessageBodyWorkers() {public Map<MediaType, List<MessageBodyReader>> getReaders(MediaType mediaType) {return null;}public Map<MediaType, List<MessageBodyWriter>> getWriters(MediaType mediaType) {return null;}public String readersToString(Map<MediaType, List<MessageBodyReader>> mediaTypeListMap) {return null;}public String writersToString(Map<MediaType, List<MessageBodyWriter>> mediaTypeListMap) {return null;}public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> tClass, Type type, Annotation[] annotations, MediaType mediaType) {return (MessageBodyReader<T>) new StringProvider();}public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> tClass, Type type, Annotation[] annotations, MediaType mediaType) {return null;}public <T> List<MediaType> getMessageBodyWriterMediaTypes(Class<T> tClass, Type type, Annotation[] annotations) {return null;}public <T> MediaType getMessageBodyWriterMediaType(Class<T> tClass, Type type, Annotation[] annotations, List<MediaType> mediaTypes) {return null;}};}public ClientBuilder content(String content) {this.content = content;return this;}
}
如果我们更改测试以使用此代码,则现在看起来像这样:
@Test
public void shouldIndicateInstanceIsSlave() {Client client = client().requestFor("http://localhost:7474/db/manage/server/ha/slave").returns(200).content("true").create();Boolean isSlave = new Neo4jInstance(client, URI.create("http://localhost")).isSlave();assertTrue(isSlave);
}
有没有更好的办法?
在Ruby中,我使用WebMock来实现这一目标,而Ashok 则将我指向WebStub ,它看起来不错,除了我需要传递主机名+端口而不是在代码中构造它。
参考: 泽西岛客户:在Mark Needham博客博客中测试 JCG合作伙伴 Mark Needham的外部呼叫 。
翻译自: https://www.javacodegeeks.com/2013/08/jersey-client-testing-external-calls.html