如何在内存中进行单元测试 Spring-Jersey
Posted
技术标签:
【中文标题】如何在内存中进行单元测试 Spring-Jersey【英文标题】:How to in-memory unit test 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
【问题讨论】:
【参考方案1】:我要解决的一些问题:
您正在使用 Jersey 1.x 风格的 Jersey 测试框架,但您的应用是 Jersey 2.x。请参阅下面的 2.x 依赖项。
我从未使用过 Jersey 1.x 风格的测试框架,但在 Jersey 2.x 中,In-Memory container 不支持依赖于 servlet 的功能。不同的依赖见下文。
使用 Jersey 测试框架,您无需自己创建 Client
。已经创建了一个,我们可以简单地调用JerseyTest
的target(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();
其他资源:
Jersey Test Framework Documentation More examples from Test Framework source code tests。 (提示:我提供的链接是针对 grizzly Web 容器示例的,但如果您返回到 providers,您可以查看每个提供程序并转到测试包以获取这些提供程序的示例)更新
所以经过几次测试后,我发现了一些有趣的事情
一个:
有了上面的依赖,即使我们不配置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
,它没有那个方法。
我同意 - 这个测试框架在幕后做了一些疯狂的魔法以上是关于如何在内存中进行单元测试 Spring-Jersey的主要内容,如果未能解决你的问题,请参考以下文章
使用带有事务的内存数据库进行单元测试时,如何抑制 InMemoryEventId.TransactionIgnoredWarning?