【问题标题】:How to in-memory unit test Spring-Jersey如何在内存中进行单元测试 Spring-Jersey
【发布时间】:2015-04-21 07:08:42
【问题描述】:

我正在使用 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

问题:依赖注入返回 null

【问题讨论】:

    标签: spring unit-testing junit jersey jersey-2.0


    【解决方案1】:

    我要解决的一些问题:

    • 您正在使用 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 上下文文件的,你可以使用一个注释配置应用程序上下文,并将其添加为一个 init 参数

    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 容器不是真正的容器。它启动 Jersey 应用程序并直接调用内部 API 来处理由测试框架提供的客户端创建的请求。不涉及网络通信。此容器不支持 servlet 和其他容器相关功能,但它是简单单元测试的完美选择。

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

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

    【讨论】:

    • 所以我试图找出这个被覆盖的方法来自于 DeploymentContext configureDeployment()。我已经碰到了几个版本的 JerseyTest 框架,但找不到这个方法的来源
    • 我会摆脱所有与 Jersey 相关的依赖项,这些依赖项不是 org.glassfish.jersey。我猜你还在使用旧的JerseyTest,它没有那个方法。
    • 我同意 - 这个测试框架在幕后做了一些疯狂的魔法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-05
    相关资源
    最近更新 更多