使用 Spring 进行依赖注入的 Jersey Web 服务集成测试无法初始化 @Context HttpServletRequest
Integration test of Jersey web services using Spring for dependency injection fails to initialize @Context HttpServletRequest
我有 jersey web 服务,它使用 spring 进行依赖注入(spring-jersey 模块)并使用 hibernate 进行对象关系映射 (ORM)。为了发展
考虑以下条件的集成测试:
- 整个测试只初始化一次测试容器class
- 将自定义侦听器、过滤器、servlet 等注册到测试中
容器
- 确保 @Context HttpServletRequest 不为空
根据这个 https://java.net/jira/browse/JERSEY-2412 泽西岛项目 JIRA 任务 HttpServletRequest 为空,如任务的解决方案所示 按设计工作。
当 运行ning 在 Grizzly 容器上进行集成测试时,它 运行 在 http 服务器上进行集成测试,因此,任何对 servlet-based[ 的依赖=39=] 有这样的功能
因为 HttpServletRequest、HttpServletResponse 等 不可用。
似乎没有关于如何解决这个问题的标准解决方案,泽西岛社区显然对贡献者开发的此类功能持开放态度,例如
在此 https://java.net/jira/browse/JERSEY-2417 JIRA 票证中说明。在实施此功能之前,可能的解决方案是什么?根据我的
研究我发现了一些帖子:
- 使用外部容器(如果我不想要怎么办?)
- 使用jersey的jetty模块(不想使用Jetty怎么办?)
- Spring不适用于此项目的 MVC 特定解决方案(因为我们不使用 Spring MVC)
那么,在使用 spring-jersey bridge 进行依赖注入并依赖于
基于 Servlet 的 功能?
这是 Jersey 的标准行为,允许 基于 servlet 的功能,例如 HttpServletRequest 尚不可用。根据我的研究,
我可以达到以下条件
- 整个测试只初始化一次测试容器class
- 将自定义侦听器、过滤器、servlet 等注册到测试中
容器
- 确保@Context HttpServletRequest 不为空
通过 starting/stopping 手动 grizzly 容器并将 grizzly 容器实例部署在基于球衣的自定义 WebappContext 上。步骤如下
如果其他人遇到这样的问题
- 创建一个 HttpServer
- 在@Before 中创建一个反映您的自定义 WebappContext
web.xml 并使用 WebappContext
部署您的 HttpServer 实例
- 为了确保Servlet-based等特性
HttpServletRequest 在集成测试期间可用使用 ServletRegistration 将您的应用程序加载到 Servlet 容器 中,如下所示
步骤#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 用于 Rest-Assured 特定目的(您不必使用它),它是可选的。
步骤#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 中的 setup 对于每个测试都是 运行 但是,if 条件将强制 setup 方法像 @BeforeClass 一样允许我们初始化
服务器一次用于整个测试 class 减去 @BeforeClass 的 static 性质。
我为测试中的所有测试初始化一次服务器的原因 class 是因为,如果我们不这样做,那么我们将有以下工作流程
- 服务器已启动
- Spring 开始扫描 bean
- Spring 自动装配
- Spring 设置sessionFactory等
- 测试是运行
- Spring 破坏上下文
- 服务器关闭
- 对每个测试重复步骤#1 到#7
以上非常耗时,技术上不可行,因此初始化容器一次(这就是为什么我不扩展 JerseyTest 因为我想控制 startup/shutdown测试容器)。
#步骤 3
@AfterClass
public static void tearDown() throws Exception {
System.out.println("Integration tests completed. Tear down the server ...");
if (server != null && 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;
我有 jersey web 服务,它使用 spring 进行依赖注入(spring-jersey 模块)并使用 hibernate 进行对象关系映射 (ORM)。为了发展 考虑以下条件的集成测试:
- 整个测试只初始化一次测试容器class
- 将自定义侦听器、过滤器、servlet 等注册到测试中 容器
- 确保 @Context HttpServletRequest 不为空
根据这个 https://java.net/jira/browse/JERSEY-2412 泽西岛项目 JIRA 任务 HttpServletRequest 为空,如任务的解决方案所示 按设计工作。 当 运行ning 在 Grizzly 容器上进行集成测试时,它 运行 在 http 服务器上进行集成测试,因此,任何对 servlet-based[ 的依赖=39=] 有这样的功能 因为 HttpServletRequest、HttpServletResponse 等 不可用。
似乎没有关于如何解决这个问题的标准解决方案,泽西岛社区显然对贡献者开发的此类功能持开放态度,例如 在此 https://java.net/jira/browse/JERSEY-2417 JIRA 票证中说明。在实施此功能之前,可能的解决方案是什么?根据我的 研究我发现了一些帖子:
- 使用外部容器(如果我不想要怎么办?)
- 使用jersey的jetty模块(不想使用Jetty怎么办?)
- Spring不适用于此项目的 MVC 特定解决方案(因为我们不使用 Spring MVC)
那么,在使用 spring-jersey bridge 进行依赖注入并依赖于 基于 Servlet 的 功能?
这是 Jersey 的标准行为,允许 基于 servlet 的功能,例如 HttpServletRequest 尚不可用。根据我的研究, 我可以达到以下条件
- 整个测试只初始化一次测试容器class
- 将自定义侦听器、过滤器、servlet 等注册到测试中 容器
- 确保@Context HttpServletRequest 不为空
通过 starting/stopping 手动 grizzly 容器并将 grizzly 容器实例部署在基于球衣的自定义 WebappContext 上。步骤如下 如果其他人遇到这样的问题
- 创建一个 HttpServer
- 在@Before 中创建一个反映您的自定义 WebappContext web.xml 并使用 WebappContext 部署您的 HttpServer 实例
- 为了确保Servlet-based等特性 HttpServletRequest 在集成测试期间可用使用 ServletRegistration 将您的应用程序加载到 Servlet 容器 中,如下所示
步骤#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 用于 Rest-Assured 特定目的(您不必使用它),它是可选的。
步骤#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 中的 setup 对于每个测试都是 运行 但是,if 条件将强制 setup 方法像 @BeforeClass 一样允许我们初始化 服务器一次用于整个测试 class 减去 @BeforeClass 的 static 性质。
我为测试中的所有测试初始化一次服务器的原因 class 是因为,如果我们不这样做,那么我们将有以下工作流程
- 服务器已启动
- Spring 开始扫描 bean
- Spring 自动装配
- Spring 设置sessionFactory等
- 测试是运行
- Spring 破坏上下文
- 服务器关闭
- 对每个测试重复步骤#1 到#7
以上非常耗时,技术上不可行,因此初始化容器一次(这就是为什么我不扩展 JerseyTest 因为我想控制 startup/shutdown测试容器)。
#步骤 3
@AfterClass
public static void tearDown() throws Exception {
System.out.println("Integration tests completed. Tear down the server ...");
if (server != null && 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;