web-dev-qa-db-fra.com

Problèmes d'exécution de JerseyTest lors de l'utilisation de HttpServletResponse

Voici un exemple de classe de ressources:

@Path("/resource") 
public class SomeResource { 
    @GET 
    @Produces({MediaType.APPLICATION_XML}) 
    public String someMethod(@QueryParam("param1") String param1, ..., @Context HttpServletRequest request) { 
            String remoteUser = request.getRemoteAddr(); 
            // Business logic here. 
            return response; 
    } 
} 

Et le JerseyTest pour la ressource:

public class TestSomeResource extends JerseyTest    { 
    @Override 
    protected Application configure() { 
            enable(TestProperties.LOG_TRAFFIC); 
            return new ResourceConfig(SomeResource.class); 
    } 

    @Test 
    public void testXMLResponse()   { 
            String response = target("resource") 
                            .queryParam("param1", param1) 
                            // More parameters here. 
                            .request() 
                            .accept(MediaType.APPLICATION_XML) 
                            .get(String.class); 
            // Some assertions on response. 
    } 
} 

Je peux exécuter des tests de jersey pour toutes les autres ressources, à l'exception de celles qui utilisent @Context HttpServletRequest comme paramètre d'entrée. Cela donne un InternalServerErrorException: HTTP 500 Internal Server Error.

Voici le stacktrace:

javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error 
    at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.Java:904) 
    at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.Java:749) 
    at org.glassfish.jersey.client.JerseyInvocation.access$500(JerseyInvocation.Java:88) 
    at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.Java:650) 
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:315) 
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:297) 
    at org.glassfish.jersey.internal.Errors.process(Errors.Java:228) 
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.Java:421) 
    at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.Java:646) 
    at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.Java:375) 
    at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.Java:275) 
    at com.mysample.TestSomeResource.testXMLResponse(TestSomeResource.Java:15) 
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:39) 
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25) 
    at Java.lang.reflect.Method.invoke(Method.Java:597) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.Java:47) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.Java:44) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.Java:17) 
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.Java:26) 
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.Java:27) 
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.Java:271) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:70) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.Java:50) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:238) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:63) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:236) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:53) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:229) 
    at org.junit.runners.ParentRunner.run(ParentRunner.Java:309) 
    at org.Eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.Java:50) 
    at org.Eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.Java:38) 
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.Java:467) 
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.Java:683) 
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.Java:390) 
    at org.Eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.Java:197) 
16
user2638465

Votre exception est liée au fait que la HttpServletRequest est null.

La documentation de Jersey dit:

3.6. Utilisation de @Context

Les sections précédentes ont introduit l'utilisation de @Context. Le chapitre 5 de la spécification JAX-RS présente tous les types Java JAX-RS standard pouvant être utilisés avec @Context.

Lors du déploiement d'une application JAX-RS à l'aide de servlet , ServletConfig, ServletContext, HttpServletRequest et HttpServletResponse sont disponibles à l'aide de @Context.

J'imagine que vous utilisez jersey-test-framework-provider-grizzly2 qui ne le supporte pas.

Si vous souhaitez avoir accès à HttpServletResponse, supprimez cette dépendance et ajoutez:

<dependency>
    <groupId>org.glassfish.jersey.test-framework</groupId>
    <artifactId>jersey-test-framework-core</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.0.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-grizzly2-servlet</artifactId>
    <version>2.1</version>
</dependency>

Maintenant, vous voulez réellement demander à JerseyTest de démarrer le bon serveur de test. Pour ce faire, vous devez remplacer une méthode protected TestContainerFactory getTestContainerFactory(). Assurez-vous de remplacer <your-Java-package> par le nom réel de votre paquet .

@Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
    return new TestContainerFactory() {
        @Override
        public TestContainer create(final URI baseUri, final ApplicationHandler application) throws IllegalArgumentException {
            return new TestContainer() {
                private HttpServer server;

                @Override
                public ClientConfig getClientConfig() {
                    return null;
                }

                @Override
                public URI getBaseUri() {
                    return baseUri;
                }

                @Override
                public void start() {
                    try {
                        this.server = GrizzlyWebContainerFactory.create(
                                baseUri, Collections.singletonMap("jersey.config.server.provider.packages", "<your-Java-package>")
                        );
                    } catch (ProcessingException e) {
                        throw new TestContainerException(e);
                    } catch (IOException e) {
                        throw new TestContainerException(e);
                    }
                }

                @Override
                public void stop() {
                    this.server.stop();
                }
            };

        }
    };
}

Vous pouvez également vérifier org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory pour une meilleure implémentation de l'usine.

21
lpiepiora

Vous pouvez également injecter un objet HttpServletRequest simulé dans la méthode configure. Voici un exemple de Jersey 1:

public abstract class AbstractIntegrationTest extends AbstractJerseyTest {

    protected HttpServletRequest httpServletRequest;

    @Override
    protected void configure(final ResourceConfig config) throws Exception {
        // create a mock and inject it via singleton provider
        httpServletRequest = mock(HttpServletRequest.class);
        config.getSingletons().add(
                new SingletonTypeInjectableProvider<Context, HttpServletRequest>(
                        HttpServletRequest.class, httpServletRequest) {});
    }

}

Jersey 2:

final HttpServletRequest request = mock(HttpServletRequest.class);
resourceConfig.register(new AbstractBinder() {
                            @Override
                            protected void configure() {
                                bind(request).to(HttpServletRequest.class);
                            }
                        });
12
Alexey Gavrilov

Donc finalement, je trouve la solution qui marche (c'est proche de la réponse la plus populaire mais avec de petits changements):


pom.xml

<properties>    
  <jersey.version>2.22.1</jersey.version>
</properties>

    <dependency>
        <groupId>org.glassfish.jersey.test-framework.providers</groupId>
        <artifactId>jersey-test-framework-provider-inmemory</artifactId>
        <version>${jersey.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-grizzly2-servlet</artifactId>
        <version>${jersey.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>

Ajoutez la classe abstraite suivante à l'application:

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.grizzly2.servlet.GrizzlyWebContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.glassfish.jersey.test.spi.TestContainer;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;

import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.Application;
import Java.io.IOException;
import Java.net.URI;
import Java.util.Collections;

public abstract class RestTest extends JerseyTest {

    @Override
    protected Application configure() {
        enable(TestProperties.LOG_TRAFFIC);
        return new ResourceConfig();
    }

    abstract protected String getRestClassName();

    @Override
    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
        return new TestContainerFactory() {
            @Override
            public TestContainer create(URI baseUri, DeploymentContext deploymentContext) {
                return new TestContainer() {
                    private HttpServer server;

                    @Override
                    public ClientConfig getClientConfig() {
                        return null;
                    }

                    @Override
                    public URI getBaseUri() {
                        return baseUri;
                    }

                    @Override
                    public void start() {
                        try {
                            this.server = GrizzlyWebContainerFactory.create(
                                    baseUri, Collections.singletonMap(ServerProperties.PROVIDER_CLASSNAMES, getRestClassName())
                            );
                        } catch (ProcessingException | IOException e) {
                            throw new TestContainerException(e);
                        }
                    }

                    @Override
                    public void stop() {
                        this.server.shutdownNow();

                    }
                };
            }
        };
    }

}

Et pour tester Reste, vous devez faire comme ceci:

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import Java.io.IOException;
import Java.net.URISyntaxException;
import Java.nio.file.Files;
import Java.nio.file.Paths;

import static org.junit.Assert.assertEquals;

public class YourRestTest extends RestTest {

    private static final Logger LOG = LoggerFactory.getLogger("TestLog");

    @Override
    protected String getRestClassName() {
        return "com.app.rest.YourRest";
    }


    @Test
    public void test() throws URISyntaxException, IOException {
        String ttt = new String(Files.readAllBytes(Paths.get(YourRestTest.class.getResource("/rest_resource/ttt.json").toURI())));
        Response response = target("/xxx").path("/yyyy").request().post(Entity.entity(ttt, "application/json"));
        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
    }
}
1
Alexey Alexeenka

Voir également les réponses de peeskillet sur ce thread de stackoverflow:[lien]

(Aucune des solutions répertoriées actuellement n'a fonctionné pour moi)

0
NS du Toit

On dirait que ce problème existe depuis longtemps. Comme l'explique @ lpiepiora, nous avons besoin d'un conteneur de test basé sur Servlet. Et il y en a déjà un dans jersey-test-framework-provider-grizzly2 (je ne sais pas s'il y en a quand la question sera posée), qui est GrizzlyWebTestContainerFactory et qui nécessite une DeploymentContext différente. Tirez sur le git le plus récent et vous trouverez un exemple dans test-framework/providers/grizzly2/src/test/Java/org/glassfish/jersey/test/grizzly/web/GrizzlyWebTest.Java . Pour être simple et précis, il vous suffit d’ajouter ces substitutions dans votre classe de test de base:

// --- For Servlet-based test container --- begins ---

@Override
protected DeploymentContext configureDeployment() {
    return ServletDeploymentContext.forServlet(new ServletContainer(new YourResourceConfig())).build();
}

@Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
    return new GrizzlyWebTestContainerFactory();
}

// --- For Servlet-based test container --- ends ---

// other stuff...

(Remplacez YourResourceConfig par votre vrai.)

EDIT: Si vous utilisez Jersey avec jersey-spring3, vous constaterez que la solution ci-dessus échoue à cause de l’absence de tous vos haricots Spring. Réparer:

@Override
protected DeploymentContext configureDeployment() {
    return ServletDeploymentContext
            .servlet(new ServletContainer(new YourResourceConfig()))
            .addListener(ContextLoaderListener.class)
            .contextParam("contextConfigLocation", "classpath:applicationContext.xml")
            .build();
}
0
fwonce