¿Está probando un servicio web JAX-RS?

84

Actualmente estoy buscando formas de crear pruebas automatizadas para un servicio web basado en JAX-RS (API Java para servicios web RESTful).

Básicamente, necesito una forma de enviarle ciertas entradas y verificar que obtengo las respuestas esperadas. Preferiría hacer esto a través de JUnit, pero no estoy seguro de cómo se puede lograr.

¿Qué enfoque utiliza para probar sus servicios web?

Actualización: como señaló entzik, desvincular el servicio web de la lógica empresarial me permite realizar una prueba unitaria de la lógica empresarial. Sin embargo, también quiero probar los códigos de estado HTTP correctos, etc.

Einar
fuente
6
Buena pregunta, sin embargo, diría que si está probando a través de HTTP, me parece que esto es una prueba de integración.
Tom Duckering
Tom. Tienes toda la razón. Deberíamos inyectar un emulador HTTP ficticio / contenedor ligero para esto. En node.js world supertest hace esto. Puede emular express.js.
Fırat KÜÇÜK

Respuestas:

34

Jersey viene con una excelente API de cliente RESTful que hace que escribir pruebas unitarias sea realmente fácil. Consulte las pruebas unitarias en los ejemplos que se envían con Jersey. Usamos este enfoque para probar el soporte REST en Apache Camel , si está interesado, los casos de prueba están aquí.

James Strachan
fuente
6
re: ahora enlace incorrecto Puede encontrar los ejemplos mencionados en la camiseta / muestras que muestran pruebas unitarias, básicamente mediante el uso de los consumidores de la camiseta para consumir recursos web. download.java.net/maven/2/com/sun/jersey/samples/bookstore/…
rogerdpack
2
Este proyecto está en GitHub, busque las pruebas en la carpeta src / test: github.com/jersey/jersey/tree/master/examples/bookstore-webapp
Venkat
2
No dudo de esta respuesta, pero me parece increíblemente divertido que Jersey siempre se abra camino en una conversación JAX-RS, cuando en algunos casos (WebSphere, desafortunadamente, para ser exactos) no está disponible y presenta el 99% de todas las respuestas aceptables. en Stack Overflow nulo y sin efecto.
26

Puede probar REST Assured, lo que hace que sea muy sencillo probar los servicios REST y validar la respuesta en Java (usando JUnit o TestNG).

Johan
fuente
1
Voté tu publicación porque la biblioteca se veía bien, pero seguro que usan muchos frascos dependientes ...
Perry Tew
17

Como dijo James; Hay un marco de prueba incorporado para Jersey. Un ejemplo simple de hola mundo puede ser así:

pom.xml para la integración de Maven. Cuando corres mvn test. Los marcos comienzan un contenedor grizzly. Puede usar jetty o tomcat cambiando dependencias.

...
<dependencies>
  <dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.16</version>
  </dependency>

  <dependency>
    <groupId>org.glassfish.jersey.test-framework</groupId>
    <artifactId>jersey-test-framework-core</artifactId>
    <version>2.16</version>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>2.16</version>
    <scope>test</scope>
  </dependency>
</dependencies>
...

ExampleApp.java

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/")
public class ExampleApp extends Application {

}

HelloWorld.java

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public final class HelloWorld {

    @GET
    @Path("/hello")
    @Produces(MediaType.TEXT_PLAIN)
    public String sayHelloWorld() {

        return "Hello World!";
    }
}

HelloWorldTest.java

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.ws.rs.core.Application;
import static org.junit.Assert.assertEquals;

public class HelloWorldTest extends JerseyTest {

    @Test
    public void testSayHello() {

        final String hello = target("hello").request().get(String.class);

        assertEquals("Hello World!", hello);
    }

    @Override
    protected Application configure() {

        return new ResourceConfig(HelloWorld.class);
    }
}

Puede consultar esta aplicación de muestra.

Fırat KÜÇÜK
fuente
Con Jersey 2.29.1, tuve que agregar jersey-hk2como dependencia porque estaba obteniendo un java.lang.IllegalStateException: InjectionManagerFactoryerror no encontrado (Ver esta pregunta ). De lo contrario, este ejemplo funciona bien.
Sarah N
7

Probablemente haya escrito algún código Java que implemente su lógica empresarial y luego haya generado el punto final de los servicios web para ello.

Una cosa importante que debe hacer es probar de forma independiente su lógica empresarial. Dado que es código java puro, puede hacerlo con pruebas JUnit regulares.

Ahora, dado que la parte de servicios web es solo un punto final, lo que debe asegurarse es que la plomería generada (stubs, etc.) esté sincronizada con su código Java. puede hacerlo escribiendo pruebas JUnit que invoquen a los clientes java del servicio web generado. Esto le permitirá saber cuándo cambia sus firmas de Java sin actualizar las cosas de los servicios web.

Si su sistema de compilación genera automáticamente la plomería de sus servicios web en cada compilación, es posible que no sea necesario probar los puntos finales (suponiendo que todo se haya generado correctamente). Depende de tu nivel de paranoia.

entzik
fuente
2
Tiene razón, aunque también necesito probar las respuestas HTTP reales que se devuelven, en particular los códigos de estado HTTP.
Einar
6

Aunque es demasiado tarde desde la fecha de publicación de la pregunta, pensé que esto podría ser útil para otras personas que tienen una pregunta similar. Jersey viene con un marco de prueba llamado Jersey Test Framework que le permite probar su servicio web RESTful, incluidos los códigos de estado de respuesta. Puede usarlo para ejecutar sus pruebas en contenedores livianos como Grizzly, HTTPServer y / o EmbeddedGlassFish. Además, el marco podría usarse para ejecutar sus pruebas en un contenedor web normal como GlassFish o Tomcat.

Bill el lagarto
fuente
¿Tiene un buen ejemplo de cómo simular controladores de llamadas? JerseyHttpCall -> MyResource -> CallHandler.getSomething () ¿Cómo podemos simular CallHandler aquí?
Balaji Boggaram Ramanarayan
3

Utilizo HTTPClient de Apache (http://hc.apache.org/) para llamar a Restful Services. La biblioteca del cliente HTTP le permite realizar fácilmente obtener, publicar o cualquier otra operación que necesite. Si su servicio usa JAXB para el enlace xml, puede crear un JAXBContext para serializar y deserializar las entradas y salidas de la solicitud HTTP.

Chris Dail
fuente
3

Eche un vistazo al generador de clientes de descanso de Alchemy . Esto puede generar una implementación de proxy para su clase de servicio web JAX-RS utilizando un cliente jersey detrás de escena. Efectivamente, llamará a sus métodos de servicio web como simples métodos java de sus pruebas unitarias. También maneja la autenticación http.

No hay generación de código involucrada si simplemente necesita ejecutar pruebas, por lo que es conveniente.

Descargo de responsabilidad: soy el autor de esta biblioteca.

Ashish Shinde
fuente
2

Mantenlo simple. Eche un vistazo a https://github.com/valid4j/http-matchers que se pueden importar desde Maven Central.

    <dependency>
        <groupId>org.valid4j</groupId>
        <artifactId>http-matchers</artifactId>
        <version>1.0</version>
    </dependency>

Ejemplo de uso:

// Statically import the library entry point:
import static org.valid4j.matchers.http.HttpResponseMatchers.*;

// Invoke your web service using plain JAX-RS. E.g:
Client client = ClientBuilder.newClient();
Response response = client.target("http://example.org/hello").request("text/plain").get();

// Verify the response
assertThat(response, hasStatus(Status.OK));
assertThat(response, hasHeader("Content-Encoding", equalTo("gzip")));
assertThat(response, hasEntity(equalTo("content")));
// etc...
keyoxy
fuente
1

Una cosa importante que debe hacer es probar de forma independiente su lógica empresarial

Ciertamente, no asumiría que la persona que escribió el código JAX-RS y está buscando realizar una prueba unitaria de la interfaz sea de alguna manera, por alguna extraña e inexplicable razón, ajena a la noción de que él o ella puede realizar pruebas unitarias en otras partes del programa, incluidas las clases de lógica empresarial. Difícilmente es útil decir lo obvio y se señaló repetidamente que las respuestas también deben ser probadas.

Tanto Jersey como RESTEasy tienen aplicaciones cliente y, en el caso de RESTEasy, puede usar las mismas anotaciones (incluso descartar la interfaz anotada y usarla en el lado del cliente y del servidor de sus pruebas).

No descanse lo que este servicio puede hacer por usted; DESCANSE lo que puede hacer por este servicio.


fuente
La gente puede querer probar algunas preocupaciones transversales. Por ejemplo, validación, autenticación, encabezados HTTP deseados, etc. De modo que la gente puede preferir probar su código JAX-RS.
Fırat KÜÇÜK
En mi aplicación, utilizo ModelMapper para "mapear" desde clases "DTO" a clases de "objeto comercial", que se entienden por las clases de "servicio" subyacentes. Este es un ejemplo de algo que sería bueno probar de forma independiente.
jkerak
Y, a veces, el subprograma REST tiene tan poca complejidad que las simulaciones serían más grandes que la capa de aplicación, como en mi caso actual. :)
tekHedd
1

Según tengo entendido, el propósito principal del autor de este problema es desacoplar la capa JAX RS de la empresarial. Y prueba unitaria solo la primera. Aquí tenemos dos problemas básicos que resolver:

  1. Ejecute la prueba de algún servidor web / de aplicaciones, coloque componentes JAX RS en él. Y solo ellos.
  2. Simulacros de servicios empresariales dentro de los componentes JAX RS / capa REST.

El primero se resuelve con Arquillian. El segundo está perfectamente descrito en arquillican y mock

Aquí hay un ejemplo del código, puede diferir si usa otro servidor de aplicaciones, pero espero que obtenga la idea básica y las ventajas.

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import com.brandmaker.skinning.service.SomeBean;

/**
* Created by alexandr on 31.07.15.
*/
@Path("/entities")
public class RestBean
{
   @Inject
   SomeBean bean;

   @GET
   public String getEntiry()
   {
       return bean.methodToBeMoked();
   }
}

import java.util.Set;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

import com.google.common.collect.Sets;

/**
*/
@ApplicationPath("res")
public class JAXRSConfiguration extends Application
{
   @Override
   public Set<Class<?>> getClasses()
   {
       return Sets.newHashSet(RestBean.class);
   }
}


public class SomeBean
{
   public String methodToBeMoked()
   {
       return "Original";
   }
}

import javax.enterprise.inject.Specializes;

import com.brandmaker.skinning.service.SomeBean;

/**
*/
@Specializes
public class SomeBeanMock extends SomeBean
{
   @Override
   public String methodToBeMoked()
   {
       return "Mocked";
   }
}

@RunWith(Arquillian.class)
public class RestBeanTest
{
   @Deployment
   public static WebArchive createDeployment() {
       WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
               .addClasses(JAXRSConfiguration.class, RestBean.class, SomeBean.class, SomeBeanMock.class)
               .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
       System.out.println(war.toString(true));
       return war;
   }

   @Test
   public void should_create_greeting() {
       Client client = ClientBuilder.newClient();
       WebTarget target = client.target("http://127.0.0.1:8181/test/res/entities");
       //Building the request i.e a GET request to the RESTful Webservice defined
       //by the URI in the WebTarget instance.
       Invocation invocation = target.request().buildGet();
       //Invoking the request to the RESTful API and capturing the Response.
       Response response = invocation.invoke();
       //As we know that this RESTful Webserivce returns the XML data which can be unmarshalled
       //into the instance of Books by using JAXB.
       Assert.assertEquals("Mocked", response.readEntity(String.class));
   }
}

Un par de notas:

  1. Aquí se utiliza la configuración JAX RS sin web.xml.
  2. El cliente JAX RS se usa aquí (no RESTEasy / Jersey, exponen una API más conveniente)
  3. Cuando comienza la prueba, el corredor de Arquillian comienza a funcionar. Aquí puede encontrar cómo configurar pruebas para Arquillian con el servidor de aplicaciones necesario.
  4. Dependiendo del servidor de aplicaciones elegido, una URL en la prueba diferirá un poco. Se puede utilizar otro puerto. 8181 es utilizado por Glassfish Embedded en mi ejemplo.

Espero que te ayude.

Alexandr
fuente
¿Este tipo de cosas es compatible con el marco de prueba integrado de Jersey? Simplemente dudo en agregar "otro marco" y todos sus frascos a mi proyecto si ya tengo algo disponible con Jersey.
jkerak