使用Guice Servlet测试项目:如何绑定超出范围的RequestScoped实例?

时间:2015-06-26 13:07:34

标签: java unit-testing junit guice guice-servlet

我们有一个使用Guice和Guice Servlet扩展实例化的Java Web应用程序。该应用程序还包括由Guice实例化的Quartz作业。我想为这些工作编写单元测试。

作业类依赖于生产代码中需要Provider<HttpServletRequest>的其他类。在生产设置中,作业可以由Guice成功实例化,并且作业按预期工作,因为它们从不在其协作者上调用任何在servlet请求提供程序上触发get的代码。这样的get调用会失败,因为作业是由某个工作线程执行的,而不是作为对servlet的HTTP请求的一部分。

现在我的问题是如何在JUnit测试中设置Guice以便我得到相同的行为,即可以实例化作业类,但是所有使用任何超出范围的@RequestScoped对象的尝试都会失败。

有两种直接的解决方案,但它们对我不起作用。

如果我没有绑定任何HttpServletRequest,我在尝试在测试设置中实例化作业时会出错:

com.google.inject.CreationException: Guice creation errors:

1) No implementation for javax.servlet.http.HttpServletRequest was bound.
  while locating com.google.inject.Provider<javax.servlet.http.HttpServletRequest>

另一方面,如果我只是绑定模拟实例,例如与

@Override
protected void configure() {
    bind(HttpServletRequest.class).toInstance(mock(HttpServletRequest.class));
}

然后可以实例化作业,但是在作业使用servlet请求实例的情况下,我不再收到测试错误。那么如何创建绑定以便Guice能够实例化提供者,但是提供者的任何使用都会失败?

1 个答案:

答案 0 :(得分:2)

经过几次尝试后,我发现了如何在测试模块中绑定@RequestScoped个对象:

@Override
protected void configure() {
    bindScope(RequestScoped.class, ServletScopes.REQUEST);

    bind(ServletRequest.class).toProvider(unusableProvider(ServletRequest.class)).in(RequestScoped.class);
    bind(HttpServletRequest.class).toProvider(unusableProvider(HttpServletRequest.class)).in(RequestScoped.class);
    bind(ServletResponse.class).toProvider(unusableProvider(ServletResponse.class)).in(RequestScoped.class);
    bind(HttpServletResponse.class).toProvider(unusableProvider(HttpServletResponse.class)).in(RequestScoped.class);
}

private static <T> Provider<T> unusableProvider(final Class<T> type) {
    return new Provider<T>() {
        @Override
        public T get() {
            throw new IllegalStateException("Unexpected call to provider of " + type.getSimpleName());
        }
    };
}

测试永远不会进入请求范围(即没有调用ServletScope.scopeRequest),因此在测试中获取RequestScoped个对象的所有尝试都会产生与生产中完全相同的错误:

com.google.inject.ProvisionException: Guice provision errors:

1) Error in custom provider, com.google.inject.OutOfScopeException: Cannot access scoped object. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter as a servlet filter for this request.

绑定“无法使用的提供商”实际上并不是必需的 - 我只是将其作为额外的安全网。这意味着此解决方案也适用于未明确绑定但使用@RequestScoped注释并由Guice自动发现的类。