Проблемы с запуском JerseyTest при работе с HttpServletResponse

Вот пример класса ресурсов:

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

И ДжерсиТест за ресурс:

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

Я могу запускать трикотажные тесты для всех других ресурсов, кроме тех, которые используют @Context HttpServletRequest в качестве входного параметра. Он дает InternalServerErrorException: HTTP 500 Internal Server Error.

Ниже приведена таблица 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) 

Ответы

Ответ 1

Вы исключение связано с тем, что HttpServletRequest - null.

Документация Джерси гласит:

3,6. Использование @Context

Предыдущие разделы ввели использование @Context. Глава 5 в спецификации JAX-RS представлены все стандартные типы Java JAX-RS которые могут использоваться с @Context.

При развертывании приложения JAX-RS с использованием сервлета, тогда ServletConfig, ServletContext, HttpServletRequest и HttpServletResponse - это доступный с помощью @Context.

Я предполагаю, что вы используете jersey-test-framework-provider-grizzly2, который не поддерживает его.

Если вы хотите получить доступ к HttpServletResponse удалить эту зависимость и добавить:

<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>

Теперь вы действительно хотите сказать JerseyTest, чтобы запустить правильный тестовый сервер, чтобы сделать это, вы должны переопределить метод protected TestContainerFactory getTestContainerFactory(). Не забудьте заменить <your-java-package> на фактическое имя вашего пакета.

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

        }
    };
}

Вы также можете проверить org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory для лучшей реализации factory.

Ответ 2

В методе configure вы также можете ввести издевательский объект HttpServletRequest. Вот пример Джерси:

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

}

Джерси 2:

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

Ответ 3

Итак, наконец, я получаю рабочее решение (оно близко к самому популярному ответу, но с небольшими изменениями):


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>

Добавьте в приложение следующий абстрактный класс:

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

                    }
                };
            }
        };
    }

}

И для тестирования Rest вам нужно сделать следующее:

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

Ответ 4

Также см. ответы peeskillet в этом потоке stackoverflow: [link]

(ни один из перечисленных в настоящее время решений не работал у меня)

Ответ 5

Похоже, эта проблема существует с давних времен. Как объяснение @lpiepiora, нам нужен тестовый контейнер на основе сервлета. И уже есть один в jersey-test-framework-provider-grizzly2 (не знаю, есть ли он, когда задан вопрос), который GrizzlyWebTestContainerFactory, и для него требуется другой DeploymentContext. Вытяните новейший git, и вы найдете пример в test-framework/providers/grizzly2/src/test/java/org/glassfish/jersey/test/grizzly/web/GrizzlyWebTest.java. Чтобы быть простым и простым, вам просто нужно добавить эти переопределения в базовый тестовый класс:

// --- 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...

(Замените YourResourceConfig на ваш реальный.)

EDIT: Если вы используете Джерси с jersey-spring3, вы обнаружите, что решение выше завершилось неудачно из-за отсутствия всего вашего Spring beans. Чтобы исправить это:

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

Ответ 6

Более простой способ - предоставить правильный конструктор factory в конструкторе тестов:

TestSomeResource() {
    super(new GrizzlyWebTestContainerFactory());
}

и предоставить контекст сервлета:

@Override
protected DeploymentContext configureDeployment() {
    return ServletDeploymentContext.forPackages("...").build(); // or other builder method
}