我在创建模拟响应对象以用于我的单元测试时遇到问题。我正在使用org.glassfish.jersey.core.jersey-client
版本 2.3.1 来实现我的 RESTful 客户端,mockito
版本 1.9.5 来帮助我处理模拟对象。这是我的测试代码:
@Test
public void testGetAll() throws IOException {
// Given
String expectedResource = "expectedResource"
final Response expectedRes = Response.ok(expectedResource, MediaType.APPLICATION_JSON).build();
String receivedResource;
BDDMockito.given(this.client.getSimpleClient().getAllWithResponse()).willReturn(expectedRes);
// When
receivedResource = this.client.getAll();
// Then
Assert.assertNotNull("Request constructed correctly and response received.", receivedResource);
Assert.assertEquals("Resource is equal to expected.", expectedResource, receivedResource);
}
执行this.client.getAll();
时出现问题。这是该方法的代码:
public String getAll() throws GenericAragornException, ProcessingException{
Response response = this.simpleClient.getAllWithResponse();
if (response.getStatus() != 200) {
processErrorResponse(response);
}
String entity = response.readEntity(String.class);
// No errors so return entity converted to resourceType.
return entity;
}
请注意,我正在用手动创建的 Response 来模拟 this.simpleClient.getAllWithResponse() 方法。当它到达response.readEntity(resourceListType);
指令时,泽西岛会抛出以下异常:java.lang.IllegalStateException - Method not supported on an outbound message.
。 经过大量的研究和调试,事实证明,出于某种原因,当我使用响应构建器(例如Response.ok(expectedResource, MediaType.APPLICATION_JSON).build();
)创建响应时,它会将其创建为出站响应而不是入站响应。后者是唯一允许使用Response.readEntity()
方法的方法。如果是出站响应,则会引发异常。
但是,我找不到任何将手动创建的响应转换为入站响应的方法。所以我的测试注定要失败:(。你们知道我能在这里做什么吗?我不想用 Mockito 嘲笑 Response 对象,因为我认为这可能是一种代码气味,因为它违反了得墨忒耳定律。真诚地说,我在这里没有想法。像这样的事情应该是简单明了的。
你也可以用 Mockito 模拟响应:
final Response response = Mockito.mock(Response.class);
Mockito.when(response.getStatus()).thenReturn(responseStatus);
Mockito.when(response.readEntity(Mockito.any(Class.class))).thenReturn(responseEntity);
Mockito.when(response.readEntity(Mockito.any(GenericType.class))).thenReturn(responseEntity);
响应状态是与响应关联的状态代码,响应Enitty当然是您要返回的实体。您可以在 Mockito 中将该模拟用作返回语句(例如......然后返回(响应))。
在我的项目中,我为模拟创建了不同类型的构建器(在这种情况下是响应),因此我可以轻松地按需构建所需的模拟,例如状态代码为 200 的响应和附加了一些自定义实体。
我有这个错误,因为当您使用 ResponseBuilder
时,它会返回一条无法用 readEntity()
处理的OutboundJaxrsResponse
消息。
我注意到只有在直接调用 Jax-RS 组件时才出现此错误。例如,如果我用@Path("/default")
注释了DefaultController
并且如果我尝试直接调用它的方法,我无法使用readEntity()
并且与您有相同的错误。
defaultController.get();
现在,当我使用 grizzly2 测试提供程序并使用客户端来定位 Rest URL 时(在前面的情况下,它是 /default
),我在响应中收到的消息是 ScopedJaxrsResponse。然后我可以使用readEntity()
方法。
target("/default").request.get();
在您的情况下,您模拟了simpleClient,以便使用未由jersey处理的ResponseBuilder
构建的响应进行回复。这相当于直接调用我的DefaultController
方法。
在不嘲笑readEntity()
方法的情况下,我建议您找到一种方法,让您的回复被泽西岛处理并变成ScopedJaxrsResponse
。
希望有帮助。
我自己遇到了这个问题,试图使用 Response.ok(entity).build()
模拟客户端到远程服务,然后允许我的客户端代码对我伪造的服务器的响应执行response.readEntity(...)
。
我在 https://codingcraftsman.wordpress.com/2018/11/26/testing-and-mocking-jersey-responses/讨论这个主题
问题是 Response.build()
方法旨在生成出站响应,该响应应在实际客户端接收和反序列化之前进行序列化。readEntity
方法期望在反序列化时调用。
正如其他海报所观察到的那样,readEntity
出站Response
将为您提供放入的确切实体对象。您甚至可以将其转换为所需的任何类型。当然,这不适用于真正的入站响应,因为入站响应仅包含来自服务器的入站流的文本/二进制文件。
编写了以下代码,以允许我使用 mockito 强制出站Response
伪装成入站:
/**
* Turn a locally made {@link Response} into one which can be used as though inbound.
* This enables readEntity to be used on what should be an outbound response
* outbound responses don't support readEntity, but we can fudge it using
* mockito.
* @param asOutbound response built as though being sent to the received
* @return a spy on the response which adds reading as though inbound
*/
public static Response simulateInbound(Response asOutbound) {
Response toReturn = spy(asOutbound);
doAnswer(answer((Class<?> type) -> readEntity(toReturn, type)))
.when(toReturn)
.readEntity(ArgumentMatchers.<Class<?>>any());
return toReturn;
}
// use the getEntity from the real object as
// the readEntity of the mock
@SuppressWarnings("unchecked")
private static <T> T readEntity(Response realResponse, Class<T> t) {
return (T)realResponse.getEntity();
}
与其使用readEntity(OutputClass.class)
,不如执行以下操作:
OutputClass entity = (OutputClass)outboundJaxrsResponse.entity
对我来说,这些答案都不起作用,因为我正在尝试编写一个服务器端单元测试来测试生成的Response
实例本身的主体。我通过调用像 String entity = readEntity(String.class)
这样的行得到了这个异常。我没有嘲笑Response
对象,而是想测试它。
我的解决方法是将上述有问题的行替换为以下内容:
String entity = (String) outboundJaxrsResponse.getEntity();
我解决了嘲笑响应的问题:
@Spy Response response;
...
// for assignments in code under testing:
doReturn( response ).when( dwConnector ).getResource( anyString() );
// for entity and response status reading:
doReturn( "{a:1}".getBytes() ).when( response ).readEntity( byte[].class );
doReturn( 200 ).when( response ).getStatus();
如果您的转向是手动创建 ScopedJaxrsResponse ,这些会有所帮助:
- http://www.javacodegeeks.com/2013/08/jersey-client-testing-external-calls.html
- http://www.smipple.net/snippet/somatik/Jersey%20REST%20client%20mocking
测试代码:
ClientResponse<?> response = response.getEntity(new GenericType<List<String>>() {});
嘲笑:
doReturn("test").when(response).getEntity(any(GenericType.class));
只需模拟OutboundJaxrsResponse#readEntity()方法,并返回您需要返回的任何内容。 例如(使用 JMockit):
new MockUp<OutboundJaxrsResponse>(){
@Mock
public String readEntity(Class<String> clazz){ return jsonValue;}
};