我在创建用于单元测试的模拟Response对象时遇到问题。我使用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);
指令时,Jersey会抛出以下异常:java.lang.IllegalStateException - Method not supported on an outbound message.
。经过大量的研究和调试后,由于某种原因,当我使用响应构建器(例如Response.ok(expectedResource, MediaType.APPLICATION_JSON).build();
)创建响应时,它会将其创建为OutboundResponse而不是 InboundResponse 。后者是唯一允许使用Response.readEntity()
方法的人。如果是 OutboundResponse ,则抛出异常。
但是,我找不到任何方法将手动创建的响应转换为InboundResponse。所以我的测试注定要失败:(。你们这些人/ gals是否知道我能在这里做什么?我不想用Mockito模仿Response对象因为我认为这可能是代码味道,因为它违反了法律规定德米特。我真诚地想到这里。这样的事情应该简单明了。
答案 0 :(得分:5)
我遇到此错误,因为当您使用ResponseBuilder
时,它会返回无法使用OutboundJaxrsResponse
处理的readEntity()
消息。
我注意到只有当我直接调用Jax-RS组件时才会出现此错误。例如,如果我DefaultController
注明@Path("/default")
并且我尝试直接调用其方法,则无法使用readEntity()
并且出现与您相同的错误。
defaultController.get();
现在,当我使用grizzly2测试提供程序并使用客户端定位Rest Url时(在前一种情况下,它是/default
),我在响应中收到的消息是ScopedJaxrsResponse。然后我可以使用readEntity()
方法。
target("/default").request.get();
在您的情况下,您模拟了simpleClient,以便使用未由jersey处理的ResponseBuilder
构建的响应进行回复。它与直接调用DefaultController
方法相当。
在不嘲笑readEntity()
方法的情况下,我建议您找一种方法让泽西处理您的回复并转换为ScopedJaxrsResponse
。
希望有所帮助。
答案 1 :(得分:3)
您还可以使用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);
responseStatus是与响应关联的状态代码,responseEnitty当然是您要返回的实体。您可以将该模拟用作Mockito中的返回语句(例如... thenReturn(response))。
在我的项目中,我为模拟创建了不同类型的构建器(在这种情况下为Response),因此我可以轻松地按需构建所需的模拟,例如:响应状态代码200和附加的某个自定义实体。
答案 2 :(得分:2)
不是使用readEntity(OutputClass.class)
,而是可以执行以下操作:
OutputClass entity = (OutputClass)outboundJaxrsResponse.entity
答案 3 :(得分:2)
对我来说,这些答案都不起作用,因为我试图编写一个服务器端单元测试来测试生成的pandas.dateframe
实例的主体本身。我通过调用像Response
这样的行来获得此异常。而不是模仿String entity = readEntity(String.class)
对象,我想测试它。
对我而言,解决方法是将上述有问题的行替换为:
Response
答案 4 :(得分:0)
我解决了嘲笑回应:
@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,这些可以提供帮助:
答案 5 :(得分:0)
测试代码:
ClientResponse<?> response = response.getEntity(new GenericType<List<String>>() {});
嘲讽:
doReturn("test").when(response).getEntity(any(GenericType.class));
答案 6 :(得分:0)
我自己遇到了这个问题,尝试使用Response.ok(entity).build()
将客户端模拟为远程服务,然后允许我的客户端代码对来自伪造服务器的响应进行response.readEntity(...)
。
我在https://codingcraftsman.wordpress.com/2018/11/26/testing-and-mocking-jersey-responses/
中讨论了这个主题问题在于Response.build()
方法旨在产生 outbound 响应,该响应旨在在被真实客户端接收并反序列化之前进行序列化。 readEntity
方法有望在反序列化时被调用。
正如其他张贴者所观察到的那样,出站readEntity
上的Response
将为您提供您放入的确切实体对象。您甚至可以将其转换为所需的任何类型。当然,这对于真正的入站响应不起作用,因为入站响应仅包含来自服务器的入站流的文本/二进制文件。
我编写了以下代码,以允许我使用模仿者来强制将出站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;
}
答案 7 :(得分:0)
只需使用您需要返回的任何内容来模拟 OutboundJaxrsResponse#readEntity() 方法。例如(使用 JMockit):
new MockUp<OutboundJaxrsResponse>(){
@Mock
public String readEntity(Class<String> clazz){ return jsonValue;}
};