如何在内存中进行单元测试Spring-Jersey

How to in-memory unit test Spring-Jersey

我正在使用 Spring-Jersey3,无法弄清楚如何使用 Spring bean

对 RESTFul API 进行单元测试

控制器

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

项目Source Code

问题:依赖注入returns null

我要解决的一些问题:

  • 您正在使用 Jersey 1.x 风格的 Jersey 测试框架,但您的应用是 Jersey 2.x。请参阅下面的 2.x 依赖关系。

  • 我从未使用过 Jersey 1.x 风格的测试框架,但是对于 Jersey 2.x,In-Memory container 不支持依赖于 servlet 的功能。请参阅下面的不同依赖关系。

  • 使用 Jersey 测试框架,您无需自己创建 Client。已经创建了一个,我们可以简单调用JerseyTesttarget(String path)方法得到一个WebTarget(Jersey 2.x,WebResource是Jersey 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,只是覆盖JerseyTest中的Application configure(),它仍然可以工作。无法真正解释它,但似乎描述符仍然被拾取。

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

两个:

即使我们去掉上面的依赖(grizzly)并使用内存中的依赖,同样简单的前面的测试仍然有效。文档说明

In-Memory container is not a real container. It starts Jersey application and directly calls internal APIs to handle request created by client provided by test framework. There is no network communication involved. This containers does not support servlet and other container dependent features, but it is a perfect choice for simple unit tests.

所以我不完全确定他们指的是什么 Servlet 特性,因为这个测试仍然有效

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

我特别不理解的是这个说法

"There is no network communication involved"

因为当我运行测试的时候,我看到一个日志

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