¿Cómo hago el código de prueba unitaria que llama a la API del Cliente de Jersey?


Escribí un código que llama a la API de cliente de Jersey que a su vez llama a un servicio web que está fuera de mi control. No quiero que mi prueba unitaria llame al servicio web real.

¿Cuál es el mejor enfoque para escribir una prueba unitaria de código que llame a la API del cliente de Jersey? ¿Debo usar la API del servidor de Jersey para escribir un servicio web JAX-RS y luego usar el Marco de pruebas de Jersey para la prueba unitaria? ¿O debería burlarme de las llamadas del servicio web de Jersey? Tengo acceso a JMock. O debería intentarlo otro enfoque?

Durante mi investigación, encontré esta discusión describiendo varias opciones, pero encontré una solución completa. ¿Hay algún ejemplo de código disponible que muestre un enfoque JUnit sugerido? No pude encontrar ninguna en la documentación de Jersey.

Aquí está el código fuente relevante:

public String getResult(URI uri) throws Exception {
  // error handling code removed for clarity
  ClientConfig clientConfig = new DefaultClientConfig();
  Client client = Client.create(clientConfig);
  WebResource service = client.resource(uri);
  String result = service.accept(accept).get(String.class);
  return result;
}

Aquí hay ejemplos de código de prueba que me gustaría aprobar. Me gustaría probar (1) pasar un URI válido y obtener una cadena válida y (2) pasar un URI no válido (por cualquier razón unre inalcanzable o no autorizado) URI y obtener una excepción de vuelta.

@Test
public void testGetResult_ValidUri() throws Exception {
  String xml = retriever.getResult(VALID_URI);
  Assert.assertFalse(StringUtils.isBlank(xml));
}

@Test(expected = IllegalArgumentException.class)
public void testGetResult_InvalidUri() throws Exception {
  retriever.getResult(INVALID_URI);
}

Todo lo anterior es la simple descripción de lo que hace mi código. En realidad, hay una capa encima que acepta dos URI, primero intenta llamar al primer URI, y si ese URI falla, entonces intenta llamar al segundo URI. Me gustaría tener pruebas unitarias que cubran (1) el primer URI tiene éxito, (2) el primer URI falla y el segundo URI tiene éxito, y (3) ambos URI fallan. Este código es lo suficientemente complejo como para probar estos diferentes escenarios utilizando JUnit, pero para hacer esto, o bien necesito ejecutar servicios web reales o burlarse de las llamadas a la API del cliente de Jersey.

Author: BennyMcBenBen, 2012-06-13

3 answers

Intente usar Mockito o Easymock para burlarse de las llamadas de servicio. Es necesario burlarse solo de estos métodos que se utilizan realmente-no hay necesidad de burlarse de todos los métodos. Puede crear un objeto mock para la clase WebResource, luego mock accept method call.

En el método de prueba @BeforeClass/@Before JUnit escriba algo como (ejemplo de Mockito)

WebResource res = mock(WebResource.class);
when(res.accept(something)).thenReturn(thatWhatYouWant);

Luego, en sus pruebas, puede usar res object como si fuera un objeto real y llamar a mock method sobre él. En lugar de devolver valor, también puede lanzar excepciones. Mockito es bastante genial.

 12
Author: Piotr Kochański,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-12 22:27:54

Normalmente lo que realmente buscas es "la forma en que uso el DSL del Cliente de Jersey produce una solicitud a la URL correcta con la carga útil y los parámetros de URL correctos". Probar esto con Mockito es muy detallado y el código de instalación generalmente terminará pareciendo algo como esto:

    when(authentication.queryParam(eq("sa"), anyBoolean())).thenReturn(testAuthentication);
    when(testAuthentication.resolveTemplate("channel", "smf")).thenReturn(testAuthentication);
    when(testAuthentication.request(
            MediaType.APPLICATION_JSON_TYPE)).thenReturn(mockRequestBuilder);
    when(mockRequestBuilder.post(any(Entity.class))).thenReturn(mockResponse);
    when(mockResponse.readEntity(ResponseWrapper.class)).thenReturn(successfulAuthResponse());

Y esto es básicamente solo para una sola solicitud de REST. Es demasiado detallado, y en lugar de probar el resultado esperado, solo está replicando los pasos que cree que son correctos al usar el Cliente de Jersey DSL.

En lugar de lo anterior, me gustaría burlarme de un servicio simple. Para esto he utilizado WireMock que inicia un servidor Jetty y donde puedo stub cosas como "esperar una solicitud a esta URL, responder con este mensaje y verificar que la carga útil es este".

Sé que esto está bordeando una prueba de integración y es un poco más lento que solo usar Mockito, pero valoro probar el resultado real y valoro la legibilidad de las pruebas mucho más en este caso.

La configuración para una prueba de cliente de Jersey basada en WireMock se ve algo como esto:

@Test
public void exactUrlOnly() {
    stubFor(get(urlEqualTo("/some/thing"))
            .willReturn(aResponse()
                .withHeader("Content-Type", "text/plain")
                .withBody("Hello world!")));

   assertThat(testClient.get("/some/thing").statusCode(), is(200));
   assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
 13
Author: vertti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-22 05:17:53

Simplemente implemente un servicio similar al trabajo y en su configuración de prueba de unidad inicie el servicio utilizando HttpServerFactory.

 0
Author: Chase,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-13 00:53:54