使用 Guice Servlet 测试项目:如何绑定范围外的 RequestScoped 实例?
Testing a project using Guice Servlet: How to bind out-of-scope RequestScoped instances?
我们有一个 Java Web 应用程序,它是使用 Guice 和 Guice Servlet 扩展实例化的。该应用程序还包括同样由 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 能够实例化提供者,但对提供者的任何使用都会失败?
经过几次尝试,我发现了如何在测试模块中绑定 @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.
实际上不需要绑定 "unusable provider" - 我只是将其作为一个额外的安全网。这意味着该解决方案也适用于未明确绑定但用 @RequestScoped
注释并由 Guice 自动发现的 类。
我们有一个 Java Web 应用程序,它是使用 Guice 和 Guice Servlet 扩展实例化的。该应用程序还包括同样由 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 能够实例化提供者,但对提供者的任何使用都会失败?
经过几次尝试,我发现了如何在测试模块中绑定 @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.
实际上不需要绑定 "unusable provider" - 我只是将其作为一个额外的安全网。这意味着该解决方案也适用于未明确绑定但用 @RequestScoped
注释并由 Guice 自动发现的 类。