使用 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)。为了发展 考虑以下条件的集成测试:

  1. 整个测试只初始化一次测试容器class
  2. 将自定义侦听器、过滤器、servlet 等注册到测试中 容器
  3. 确保 @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 票证中说明。在实施此功能之前,可能的解决方案是什么?根据我的 研究我发现了一些帖子:

  1. 使用外部容器(如果我不想要怎么办?)
  2. 使用jersey的jetty模块(不想使用Jetty怎么办?)
  3. Spring不适用于此项目的 MVC 特定解决方案(因为我们不使用 Spring MVC)

那么,在使用 spring-jersey bridge 进行依赖注入并依赖于 基于 Servlet 的 功能?

这是 Jersey 的标准行为,允许 基于 servlet 的功能,例如 HttpServletRequest 尚不可用。根据我的研究, 我可以达到以下条件

  1. 整个测试只初始化一次测试容器class
  2. 将自定义侦听器、过滤器、servlet 等注册到测试中 容器
  3. 确保@Context HttpServletRequest 不为空

通过 starting/stopping 手动 grizzly 容器并将 grizzly 容器实例部署在基于球衣的自定义 WebappContext 上。步骤如下 如果其他人遇到这样的问题

  1. 创建一个 HttpServer
  2. 在@Before 中创建一个反映您的自定义 WebappContext web.xml 并使用 WebappContext
  3. 部署您的 HttpServer 实例
  4. 为了确保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 减去 @BeforeClassstatic 性质。

我为测试中的所有测试初始化​​一次服务器的原因 class 是因为,如果我们不这样做,那么我们将有以下工作流程

  1. 服务器已启动
  2. Spring 开始扫描 bean
  3. Spring 自动装配
  4. Spring 设置sessionFactory等
  5. 测试是运行
  6. Spring 破坏上下文
  7. 服务器关闭
  8. 对每个测试重复步骤#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;