Testing a project with Guice Servlet: how to bind external objects to RequestScoped instances?

We have a Java web application that is built using Guice and the Guice Servlet extension. The app also includes Quartz jobs, which are also generated by Guice. I want to write a unit test for these jobs.

Job classes depend on other other classes from our production code that requires Provider<HttpServletRequest>

. In a productive setup, the jobs can be successfully generated by Guice, and the work orders work as expected because they never call the code for their collaborators that runs the get

servlet requests in the server. Such a call get

failed because the jobs are being executed by some kind of worker thread and not as part of an HTTP request to the servlet.

Now my question is how to set up Guice in a JUnit test so that I get the same behavior i.e. job classes can be created, but all attempts to use any objects out of scope @RequestScoped

will fail.


There are two solutions based on simple solutions, but they don't work for me.

If I don't bind any HttpServletRequest

, I get an error when I try to instantiate a job in a test setup:

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>

      

If, on the other hand, I am just linking dummy instances eg. from

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

      

then the job can be created, but I no longer get a test error if the job uses a servlet request instance. So how can I create a binding so that Guice can instantiate providers, but any use of providers would fail?

+3


source to share


1 answer


After several attempts, I found out how to bind objects @RequestScoped

in a test unit:

@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());
        }
    };
}

      

Tests never enter the scope of the request (i.e., there are no calls to ServletScope.scopeRequest

), so all attempts to get objects RequestScoped

in tests will result in exactly the same error as in production:



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.

      

The "unusable vendor" binding is not really required - I just did it as additional security. This means that this solution should also work for classes that are not explicitly related but are annotated with @RequestScoped

and automatically detected by Guice.

+2


source







All Articles