无法模拟玻璃鱼泽西岛客户端响应对象



我在创建模拟响应对象以用于我的单元测试时遇到问题。我正在使用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;}
    };

相关内容

  • 没有找到相关文章

最新更新