Тест интеграции веб-сервисов Джерси с использованием Spring для внедрения зависимостей не удается инициализировать @Context HttpServletRequest

#java #spring #integration-testing #jersey-2.0 #jersey-test-framework

#java #spring #интеграция-тестирование #джерси-2.0 #jersey-test-framework

Вопрос:

У меня есть веб-сервисы Джерси, которые используют spring для внедрения зависимостей (модуль spring-jersey) и спящий режим для объектно-реляционного отображения (ORM). Для разработки интеграционных тестов с учетом следующих условий:

  1. Запустите тестовый контейнер только один раз для всего тестового класса
  2. Зарегистрируйте пользовательские прослушиватели, фильтры, сервлеты и т. Д. В тестовом контейнере
  3. Убедитесь, что @Context HttpServletRequest не равен null

В соответствии с этим https://java.net/jira/browse/JERSEY-2412 Jersey project JIRA task HttpServletRequest имеет значение null, и это, как показано в разрешении задачи, работает так, как определено. При запуске интеграционных тестов в контейнере Grizzly он запускает интеграционные тесты на http-сервере, следовательно, любая зависимость от функций на основе сервлетов, таких как HttpServletRequest, HttpServletResponse и т. Д., недоступна.

Похоже, нет стандартного решения о том, как решить эту проблему, и сообщество Джерси, очевидно, открыто для таких функций, разрабатываемых участниками, как указано в этом https://java.net/jira/browse/JERSEY-2417 Билет JIRA. Каковы возможные обходные решения, пока эта функция не будет реализована? Основываясь на моих исследованиях, я натыкаюсь на несколько сообщений, в которых говорится:

  1. Используйте внешний контейнер (что, если я тоже не хочу?)
  2. Используйте модуль jetty от jersey (что, если я не хочу использовать Jetty?)
  3. Решения, специфичные для SpringMVC, которые не применяются к этому проекту (поскольку мы не используем Spring MVC)

Итак, каково наилучшее решение для успешного запуска интеграционных тестов на веб-серверах на основе джерси, которые используют мост spring-jersey для внедрения зависимостей и полагаются на функции на основе сервлетов?

Ответ №1:

Это стандартное поведение Джерси, и разрешить функции на основе сервлетов, такие как HttpServletRequest, пока недоступно. Основываясь на моих исследованиях, я мог бы выполнить следующие условия

  1. Запустите тестовый контейнер только один раз для всего тестового класса
  2. Зарегистрируйте пользовательские прослушиватели, фильтры, сервлеты и т. Д. В тестовом контейнере
  3. Убедитесь, что @Context HttpServletRequest не равен null

Путем запуска / остановки контейнера grizzly вручную и развертывания экземпляра контейнера grizzly в пользовательском WebAppContext на основе джерси. На случай, если кто-нибудь еще столкнется с такой проблемой, выполните следующие действия

  1. Создайте HTTPServer
  2. В @Before создайте пользовательский WebAppContext, который отражает ваш web.xml и разверните свой экземпляр HTTPServer с помощью WebAppContext
  3. Чтобы убедиться, что функции на основе сервлетов, такие как HttpServletRequest, доступны во время интеграционного теста, используйте ServletRegistration для загрузки вашего приложения в контейнер сервлета, как показано ниже

Шаг #1

 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class ResourceEndpointIntegrationTest{
    @Context
    private HttpServletRequest httpReq;

    private static Logger logger = Logger.getLogger(ResourceEndpointIntegrationTest.class);

    public static final String BASE_URI = "http://localhost:8989/";
    private static HttpServer server = null;

    @BeforeClass
    public static void initTest() {
        RestAssured.baseURI = "http://localhost:8989/";
    }
...
}
  

Использовать SpringJUnit4ClassRunner.class и @ContextConfiguration для загрузки applicationContext.xml для проведения тестов. Также объявите @Context HttpServletRequest и создайте
экземпляр HTTPServer для последующего использования. Я использую @BeforeClass здесь для определенных целей (вам не обязательно его использовать), это необязательно.

Шаг #2

 @Before
    public void setup() throws Exception {
        if (server == null) {
            System.out.println("Initializing an instance of Grizzly Container ...");
            final ResourceConfig rc = new ResourceConfig(ResourceEndpointIntegrationTest.class, ..., ..., ...); //update

            WebappContext ctx = new WebappContext("IntegrationTestContext");
                        //register your listeners from web.xml in here
            ctx.addListener("com.xxx.yyy.XEndpointServletContextListener");
                        //register your applicationContext.xml here
            ctx.addContextInitParameter("contextConfigLocation", "classpath:applicationContext.xml");

                        //ServletRegistration is needed to load the ResourceConfig rc inside ServletContainer or you will have no 
                        //Servlet-based features available 
            ServletRegistration registration = ctx.addServlet("ServletContainer",
                    new ServletContainer(rc));

                        //Initialize the Grizzly server passing it base URL
            server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI));

                        //Deploy the server using our custom context
            ctx.deploy(server);
        }
    }
  

Настройка в @Before выполняется для каждого теста, но условие if заставит метод setup действовать как @BeforeClass, позволяя нам инициализировать
сервер один раз для всего тестового класса за вычетом статического характера @BeforeClass .

Причина, по которой я инициализирую сервер один раз для всех тестов внутри тестового класса, заключается в том, что, если мы этого не сделаем, у нас будет следующий рабочий процесс

  1. Запущен сервер
  2. Spring запускает сканирование на наличие компонентов
  3. Spring автоматически подключается
  4. Spring настраивает SessionFactory и т. Д.
  5. Тест запущен
  6. Spring разрушает контекст
  7. Сервер завершает работу
  8. Повторите шаги с # 1 по # 7 для каждого теста

Вышеизложенное занимает очень много времени и технически невозможно, поэтому инициализируйте контейнер один раз (именно поэтому я не расширяю JerseyTest, потому что я хочу контролировать запуск / завершение работы тестового контейнера).

#Шаг 3

 @AfterClass
    public static void tearDown() throws Exception {
        System.out.println("Integration tests completed. Tear down the server ...");
        if (server != null amp;amp; server.isStarted()) {
            server.shutdownNow();
            System.out.println("Grizzly instance shutdown completed");
        }
    }
  

На шаге 3 мы используем @AfterClass для завершения работы экземпляра grizzly, используемого для целей тестирования интеграции.

И пример теста выглядит следующим образом

 @Test
    public void testCreateSomethingReturnSuccessfully() {
        JSONObject something = new JSONObject();
        cust.put("name", "integrationTest");
        cust.put("age", 33);

        given().
            contentType(ContentType.JSON).
            body(something.toString()).post("/someEndpoint").
        then().
            statusCode(200).
        assertThat().
            body("id", greaterThan(0)).
            body("name", equalTo("integrationTest")).
            body("age", equalTo(33));
    }
  

(Gradle) Некоторые из соответствующих зависимостей

 compile group: 'org.glassfish.jersey.containers', name: 'jersey-container-servlet', version: '2.23.2'
compile group: 'org.glassfish.jersey.test-framework.providers', name:'jersey-test-framework-provider-grizzly2', version:'2.23.2'
compile group: 'org.springframework', name:'spring-test', version:'4.3.2.RELEASE'
compile group: 'io.rest-assured', name:'rest-assured', version:'3.0.1'


// Spring
    compile group: 'org.springframework', name: 'spring-core', version: '4.3.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-beans', version: '4.3.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-web', version: '4.3.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-jdbc', version: '4.3.2.RELEASE'
    compile group: 'org.springframework', name: 'spring-orm', version: '4.3.2.RELEASE'

    // Jersey-Spring bridge
    compile (group: 'org.glassfish.jersey.ext', name: 'jersey-spring3', version: '2.23.2'){
        exclude group: 'org.springframework', module: 'spring-core'
        exclude group: 'org.springframework', module: 'spring-web'
        exclude group: 'org.springframework', module: 'spring-beans'
        exclude group: 'org.springframework', module: 'spring-jdbc'
        exclude group: 'org.springframework', module: 'spring-orm'
    }
  

Некоторые импортные

 import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.servlet.ServletRegistration;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;