Testujesz usługę sieciową JAX-RS?

84

Obecnie szukam sposobów tworzenia automatycznych testów dla usługi internetowej opartej na JAX-RS (Java API for RESTful Web Services).

Zasadniczo potrzebuję sposobu, aby przesłać mu określone dane wejściowe i sprawdzić, czy otrzymuję oczekiwane odpowiedzi. Wolałbym to zrobić za pośrednictwem JUnit, ale nie jestem pewien, jak można to osiągnąć.

Jakiego podejścia używasz do testowania swoich usług internetowych?

Aktualizacja: Jak zauważył entzik, oddzielenie usługi sieciowej od logiki biznesowej pozwala mi na testowanie jednostkowe logiki biznesowej. Jednak chcę również przetestować prawidłowe kody stanu HTTP itp.

Einar
źródło
6
Dobre pytanie - powiedziałbym jednak, że jeśli testujesz przez HTTP, to wydaje mi się, że jest to testowanie integracji.
Tom Duckering
Tomek. Masz absolutną rację. Powinniśmy w tym celu wstrzyknąć emulator HTTP / lekki kontener. W supertestach świata node.js to robi. Możesz emulować express.js.
Fırat KÜÇÜK

Odpowiedzi:

34

Jersey jest wyposażony w świetne API klienta RESTful, które sprawia, że ​​pisanie testów jednostkowych jest naprawdę łatwe. Zobacz testy jednostkowe w przykładach dostarczanych z Jersey. Używamy tego podejścia do testowania obsługi REST w Apache Camel , jeśli jesteś zainteresowany, przypadki testowe są tutaj

James Strachan
źródło
6
re: now zły link Przykłady wymienione w koszulkach / próbkach, które pokazują testy jednostkowe, w zasadzie wykorzystują konsumentów jersey do konsumowania zasobów sieciowych. download.java.net/maven/2/com/sun/jersey/samples/bookstore/…
rogerdpack
2
Ten projekt jest na GitHub, znajdź testy w folderze src / test: github.com/jersey/jersey/tree/master/examples/bookstore-webapp
Venkat
2
Nie wątpię w tę odpowiedź, ale uważam za niesamowicie zabawne, że Jersey zawsze trafia do konwersacji JAX-RS, kiedy w niektórych przypadkach (niestety WebSphere, dokładniej mówiąc) jest niedostępna i renderuje 99% wszystkich akceptowalnych odpowiedzi w przypadku przepełnienia stosu null and void.
26

Możesz wypróbować REST Assured, który bardzo ułatwia testowanie usług REST i sprawdzanie poprawności odpowiedzi w Javie (za pomocą JUnit lub TestNG).

Johan
źródło
1
Głosowałem za twoim postem, ponieważ biblioteka wyglądała dobrze, ale z pewnością używają wielu zależnych słoików ...
Perry Tew
17

Jak powiedział James; Jest wbudowany framework testowy dla Jersey. Prosty przykład Hello World może wyglądać tak:

pom.xml do integracji maven. Kiedy biegasz mvn test. Struktury rozpoczynają kontener grizzly. Możesz użyć jetty lub tomcat poprzez zmianę zależności.

...
<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);
    }
}

Możesz sprawdzić przykładową aplikację.

Fırat KÜÇÜK
źródło
W przypadku Jersey 2.29.1 musiałem dodać jersey-hk2jako zależność, ponieważ otrzymywałem java.lang.IllegalStateException: InjectionManagerFactorybłąd „nie znaleziono” (zobacz to pytanie ). W przeciwnym razie ten przykład działa dobrze.
Sarah N.
7

Prawdopodobnie napisałeś kod java, który implementuje logikę biznesową, a następnie wygenerowałeś dla niego punkt końcowy usług internetowych.

Ważną rzeczą do zrobienia jest niezależne przetestowanie logiki biznesowej. Ponieważ jest to czysty kod java, możesz to zrobić za pomocą regularnych testów JUnit.

Teraz, ponieważ część usług sieci Web jest tylko punktem końcowym, chcesz się upewnić, że wygenerowana instalacja hydrauliczna (odgałęzienia itp.) Jest zsynchronizowana z kodem Java. możesz to zrobić, pisząc testy JUnit, które wywołują wygenerowane klienty Java usługi WWW. Dzięki temu będziesz wiedzieć, kiedy zmienisz swoje podpisy java bez aktualizowania usług internetowych.

Jeśli instalacja hydrauliczna usług internetowych jest automatycznie generowana przez system kompilacji przy każdej kompilacji, może nie być konieczne testowanie punktów końcowych (zakładając, że wszystko jest poprawnie wygenerowane). Zależy od poziomu paranoi.

entzik
źródło
2
Masz rację, chociaż muszę również przetestować rzeczywiste odpowiedzi HTTP, które są zwracane, w szczególności kody stanu HTTP.
Einar
6

Chociaż jest za późno od daty wysłania pytania, pomyślałem, że może to być przydatne dla innych, którzy mają podobne pytanie. Jersey jest dostarczany z platformą testową zwaną Jersey Test Framework, która umożliwia testowanie usługi internetowej RESTful, w tym kodów stanu odpowiedzi. Możesz go użyć do uruchomienia testów na lekkich kontenerach, takich jak Grizzly, HTTPServer i / lub EmbeddedGlassFish. Framework może być również używany do uruchamiania testów w zwykłym kontenerze WWW, takim jak GlassFish lub Tomcat.

Bill the Lizard
źródło
Czy masz dobry przykład, jak kpić z obsługi połączeń? JerseyHttpCall -> MyResource -> CallHandler.getSomething () Jak możemy kpić z CallHandler tutaj?
Balaji Boggaram Ramanarayan,
3

Używam HTTPClient Apache (http://hc.apache.org/) do wywoływania Restful Services. Biblioteka klienta HTTP umożliwia łatwe wykonywanie operacji pobierania, wysyłania lub innych potrzebnych operacji. Jeśli usługa używa JAXB do powiązania XML, możesz utworzyć JAXBContext w celu serializacji i deserializacji danych wejściowych i wyjściowych z żądania HTTP.

Chris Dail
źródło
3

Spójrz na generator klienta reszty Alchemy . Może to wygenerować implementację proxy dla Twojej klasy usług sieciowych JAX-RS przy użyciu klienta koszulki za kulisami. Skutecznie będziesz wywoływać metody usługi sieciowej jako proste metody java z testów jednostkowych. Obsługuje również uwierzytelnianie HTTP.

Nie ma potrzeby generowania kodu, jeśli chcesz po prostu uruchomić testy, więc jest to wygodne.

Zastrzeżenie: jestem autorem tej biblioteki.

Ashish Shinde
źródło
2

Nie komplikuj. Spójrz na https://github.com/valid4j/http-matchers, które można zaimportować z Maven Central.

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

Przykład użycia:

// 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
źródło
1

Ważną rzeczą do zrobienia jest niezależne przetestowanie logiki biznesowej

Z pewnością nie zakładałbym, że osoba, która napisała kod JAX-RS i chce przeprowadzić testy jednostkowe interfejsu, jest w jakiś sposób, z jakiegoś dziwnego, niewytłumaczalnego powodu, obojętna na to, że może jednostkowo testować inne części programu, w tym klasy logiki biznesowej. Stwierdzenie tego, co oczywiste, nie jest pomocne, a wielokrotnie wskazywano, że odpowiedzi również należy przetestować.

Zarówno Jersey, jak i RESTEasy mają aplikacje klienckie, aw przypadku RESTEasy możesz użyć tych samych adnotacji (nawet uwzględnij interfejs z adnotacjami i użyj po stronie klienta i serwera w testach).

REST nie tego, co ta usługa może zrobić dla Ciebie; POCZUJ, co możesz zrobić dla tej usługi.


źródło
Ludzie mogą chcieć przetestować pewne kwestie przekrojowe. Na przykład walidacja, uwierzytelnianie, żądane nagłówki HTTP itp. Dlatego ludzie mogą preferować testowanie swojego kodu JAX-RS.
Fırat KÜÇÜK
W mojej aplikacji używam ModelMapper do „mapowania” z klas „DTO” na klasy „obiektów biznesowych”, które są rozumiane przez podstawowe klasy „usług”. To jest przykład czegoś, co dobrze byłoby przetestować samodzielnie.
jkerak
Czasami aplet REST ma tak małą złożoność, że makiety byłyby większe niż warstwa aplikacji, jak w moim obecnym przypadku. :)
tekHedd
1

Jak rozumiem, głównym celem autora tego wydania jest oddzielenie warstwy JAX RS od biznesowej. Test jednostkowy tylko pierwszy. Tutaj musimy rozwiązać dwa podstawowe problemy:

  1. Uruchom w teście jakiś serwer WWW / aplikacyjny, umieść w nim komponenty JAX RS. I tylko oni.
  2. Mock usługi biznesowe wewnątrz komponentów JAX RS / warstwy REST.

Pierwszą rozwiązuje Arquillian. Drugi jest doskonale opisany w arquillican i mock

Oto przykład kodu, może się różnić, jeśli używasz innego serwera aplikacji, ale mam nadzieję, że uzyskasz podstawowy pomysł i zalety.

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));
   }
}

Kilka uwag:

  1. Używana jest tutaj konfiguracja JAX RS bez web.xml.
  2. Używany jest tutaj klient JAX RS (bez RESTEasy / Jersey, udostępniają wygodniejsze API)
  3. Kiedy zaczyna się test, biegacz Arquilliana zaczyna działać. Tutaj możesz dowiedzieć się, jak skonfigurować testy dla Arquilliana z wymaganym serwerem aplikacji.
  4. W zależności od wybranego serwera aplikacji adres URL w teście będzie się nieco różnić. Można użyć innego portu. W moim przykładzie 8181 jest używany przez Glassfish Embedded.

Mam nadzieję, że to pomoże.

Alexandr
źródło
czy tego typu rzeczy są obsługiwane przez wbudowany framework testowy dla koszulek? Waham się, czy dodać „jeszcze jeden framework” i wszystkie jego pliki do mojego projektu, jeśli mam już coś dostępnego w Jersey.
jkerak