Как провести в памяти юнит-тест Spring-Jersey

Я работаю с Spring-Jersey3 и не могу понять, как выполнить модульное тестирование RESTFul API с бобами Spring

контроллер

package com.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.service.DataSource;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("test")
@Component
public class SpringController {
    @Autowired
    private DataSource datasource;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getHello() {
        return new String(datasource.load());
    }
}

Сервисный интерфейс

package com.service;

public interface DataSource {
    public String load();
}

Внедрение сервиса

package com.service;

import org.springframework.stereotype.Repository;

@Repository
public class DataSourceImpl implements DataSource {

    @Override
    public String load() {
        return "Hello";
    }
}

ResourceRegister.java (реестр ресурсов Джерси)

package com.component;

import org.glassfish.jersey.server.ResourceConfig;
import com.controller.SpringController;

public class ResourceRegister extends ResourceConfig {

    public ResourceRegister () {
        register(SpringController.class);
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<servlet>
  <servlet-name>Jersey</servlet-name>
  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
  <param-name>javax.ws.rs.Application</param-name>
  <param-value>com.component.ResourceRegister</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>Jersey</servlet-name>
  <url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

serviceContext.xml (контекст приложения)

<?xml version="1.0" encoding="UTF-8"?>

<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="com.service" />
<context:component-scan base-package="com.controller" />

</beans>

Модульный тест << - я действительно не знаю, как это проверить

public class test extends JerseyTest {
    public test() {
        super("com.service", "com.controller");
    }

    @Override
    protected AppDescriptor configure() {
        return new WebAppDescriptor.Builder("com.service","com.controller")
               .contextParam("contextConfigLocation", "classpath:serviceContext.xml")
               .contextPath("/rest")
               .servletClass("org.glassfish.jersey.servlet.ServletContainer.class")
               .initParam("javax.ws.rs.Application", "com.component.ResourceRegister")
               .build();
    }

    @Test
    public void test() {
        Client client = new Client();
        WebResource resource = client.resource("test");

        ClientResponse response = resource.post(ClientResponse.class);

        assertEquals(200, resposne.getStatus());
    }
}

Исходный код проекта

Проблема: внедрение зависимостей возвращает ноль

1 ответ

Решение

Несколько вещей, которые я бы исправил:

  • Вы используете Jersey 1.x со структурой Jersey Test Framework, но ваше приложение - Jersey 2.x. Смотрите ниже для 2.x зависимости.

  • Я никогда не использовал тестовую среду Jersey 1.x, но в Jersey 2.x контейнер In-Memory не поддерживает функции, зависящие от сервлетов. Смотрите ниже для другой зависимости.

  • С Jersey Test Framework вам не нужно создавать Client сам. Существует один созданный, и мы можем просто позвонить JerseyTest"s target(String path) способ вернуть WebTarget (Джерси 2.х, WebResource это Джерси 1.x)

Вот рефакторинг, который работает.

Зависимость (я только добавил эту зависимость и ничего не убрал, так как ваш проект GitHub не включал в себя ничего связанного с тестом, как в вашем примере кода выше)

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

Тестовое задание

import com.component.ResourceRegister;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.context.ContextLoaderListener;

public class SpringTest extends JerseyTest {

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

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

    @Test
    public void test() {
        String response = target("test").request().get(String.class);
        Assert.assertEquals("Hello", response);
        System.out.println(response);
    }  
}

Для тех, кто не использует файл контекста xml, вы можете использовать контекст приложения конфигурации аннотации и добавить его в качестве параметра инициализации

return ServletDeploymentContext
        .forServlet(new ServletContainer(new ResourceRegister()))
        .addListener(ContextLoaderListener.class)
        .initParam("contextConfig", new AnnotationConfigApplicationContext(YourSpringConfig.class))
        .build();

Другие источники:


ОБНОВИТЬ

Итак, после еще нескольких испытаний, вот пара интересных вещей, которые я обнаружил

Один:

С учетом вышеуказанной зависимости, даже если мы не настроим DeploymentContextи просто переопределить Application configure() в JerseyTest, это все еще будет работать. Не могу объяснить это, но кажется, дескриптор все еще поднят.

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

public class SpringTest extends JerseyTest {

    @Override
    public Application configure() {
        return new ResourceConfig().packages("com.controller");
    }

    @Test
    public void test() {
        String response = target("test").request().get(String.class);
        Assert.assertEquals("Hello", response);
        System.out.println(response);
    }  
}

Два:

Даже если мы избавимся от вышеуказанной зависимости (гризли) и используем зависимость в памяти, этот же простой предыдущий тест будет работать. Документация гласит

Контейнер In-Memory не является реальным контейнером. Он запускает приложение в Джерси и напрямую вызывает внутренние API-интерфейсы для обработки запроса, созданного клиентом, предоставляемым тестовой средой. Нет сетевого взаимодействия. Эти контейнеры не поддерживают сервлет и другие зависимые от контейнера функции, но это идеальный выбор для простых модульных тестов.

Поэтому я не совсем уверен, на какие функции сервлета они ссылаются, так как этот тест все еще работает

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-inmemory</artifactId>
    <version>2.15</version>
</dependency>

Что я не понимаю, в частности, это утверждение

"Нет связи с сетью"

потому что когда я запускаю тест, я вижу журнал

INFO: Creating InMemoryTestContainer configured at the base URI http://localhost:9998/

Другие вопросы по тегам