Search code examples
javaunit-testingmavenguice-3

Google guice module configuration for unit testing


I'm trying to use google guice for dependency injection however I can't seem to wire everything togheter.

In my web.xml I defined the guiceFilter and the guiceListener like so:

   <filter>
    <filter-name>guiceFilter</filter-name>
    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>guiceFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
    <listener-class>backend.listener.GuiceConfigListener</listener-class>
</listener>

the config listener is basicly pretty simple:

@Override
protected Injector getInjector(){
    return Guice.createInjector(new ServletModule(), new ArtsModule());
}

and the ArtsModule at this moment just has one binding like so:

@Override
protected void configure(){
    bind(ArtsDAO.class).to(ArtsDAOGae.class);
}

I then continue to do a field injection of the ArtsDao in a service class:

@Inject
private ArtsDAO artsDAO;

But when I try to build my project (which is a maven build) I get a NPE on the artsDAO field, this most likely happens because the unit tests aren't running in a web environment.

Can anyone advice me on how to configure the guice bidings so that they are picked up during unit testing?

Thanks


Solution

  • Pip,

    this is not trivial task but definitely you can achieve what you want.

    First of all have a look at Tadedon project at https://code.google.com/p/tadedon especially tadedon-guice-servlet-mock.

    You will need something like fake container for your test. My fake container contains also Apache Shiro integration so you can throw it out, It looks like:

    import com.google.inject.Guice;
    import com.google.inject.Injector;
    import com.google.inject.Key;
    import com.google.inject.Module;
    import com.xemantic.tadedon.guice.servlet.mock.FakeServletContainer;
    import com.xemantic.tadedon.guice.servlet.mock.FakeServletContainerModule;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.subject.support.SubjectThreadState;
    import org.apache.shiro.web.subject.WebSubject;
    import org.springframework.mock.web.MockHttpServletRequest;
    import org.springframework.mock.web.MockHttpServletResponse;
    
    import javax.servlet.ServletException;
    import java.io.IOException;
    import java.util.Arrays;
    public class FakeTestContainerInit {
    
        private final FakeServletContainer servletContainer;
        private final Injector internalInjector;
        private Subject internalSubject;
    
        public FakeTestContainerInit() {
            this(new Module[] {});
        }
    
        public FakeTestContainerInit(Module... modules) {
            super();
    
            modules = Arrays.copyOf(modules, modules.length + 1);
            modules[modules.length-1] = new FakeServletContainerModule();
            internalInjector = Guice.createInjector(modules);
            servletContainer = internalInjector.getInstance(FakeServletContainer.class);
        }
    
        public void start() throws ServletException, IOException {
            this.start(true);
        }
    
        public void start(boolean initializeSecurityContext) throws ServletException, IOException {
    
            getServletContainer().start();
    
            MockHttpServletRequest request = servletContainer.newRequest("GET","/");
            MockHttpServletResponse response = new MockHttpServletResponse();
    
            if(initializeSecurityContext) {
                SecurityManager scm = internalInjector.getInstance(SecurityManager.class);
                internalSubject = new WebSubject.Builder(scm, request, response).buildWebSubject();
                SubjectThreadState sts = new SubjectThreadState(internalSubject);
                sts.bind();
            } else { internalSubject = null; }
    
            getServletContainer().service(request, response);
    
        }
    
        public void stop() {
            servletContainer.stop();
        }
    
        public FakeServletContainer getServletContainer() {
            return servletContainer;
        }
    
        public <T> T getInstance(final Class<T> type) throws IOException, ServletException {
            return getServletContainer().getInstance(type);
        }
    
        public <T> T getInstance(final Key<T> key) throws IOException, ServletException {
            return getServletContainer().getInstance(key);
        }
    
        public Subject getSubject() {
            return internalSubject;
        }
    }
    

    Dependencies:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.sonatype.sisu</groupId>
            <artifactId>sisu-guice</artifactId>
        </dependency>
        <dependency>
            <groupId>com.xemantic.tadedon</groupId>
            <artifactId>tadedon-guice-servlet-mock</artifactId>
        </dependency>
    

    and Apache Shiro you won't need:

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
        </dependency>
    

    All you need to do, is create FakeTestContainerInit and call start() and stop() method. Also all object creations have to be done via FakeTestContainerInit.getInstance method inside tests.

    Well, I used it to test Vaadin application so I did not need sending requests and checking responses. So, this one you will need to implement. It can be done via getServletContainer().service(request, response);. But i think you will figure out. Hope it will help you.