Dependency injection is an integral part of a typical Java application. ActiveWeb supports seamless integration with Google Guice.

At the heart of a Google Guice DI, there is a concept of a module.

Creation of a Guice module

Lets say we have a simple interface Greeter:

1
2
3
public interface Greeter {
    String greet();
}

and implementation of this interface:

1
2
3
4
5
public class GreeterImpl implements Greeter{    
    public String greet() {
        return "Hello from real greeter";        
    }
}

We can then create a new Guice module:

1
2
3
4
5
public class GreeterModule extends AbstractModule {
    protected void configure() {
        bind(Greeter.class).to(GreeterImpl.class).asEagerSingleton();
    }
}

In this module, we are binding a GreeterImpl to Greater interface as a singleton. You can call bind() method many times, setting up your object graph.

Injection of module into the application

The injection of a Guice module is executed as one line of code inside AppBootstrap class, like so:

1
2
3
4
5
6
7
8
public class AppBootstrap extends Bootstrap {

    public void init(AppContext context) {}

    protected Injector getInjector(){
       return Guice.createInjector(new GreeterModule());
    }
}

The Guice.createInjector(..) takes a varargs, meaning you can inject multiple modules at once.

Consumption of injected dependencies

Whenever you need a service inside a controller, you will use an @Inject annotation provided by Google Guice:

1
2
3
4
5
6
7
public class HelloController extends AppController {
 @Inject
 private Greeter greeter;
 public void index(){
     view("message", greeter.greet());
 }
}

The greeter (line 3) method is set by the framework and injected an instance of a GreeterImpl into the HelloController controller just before it executes an action. Once the controller has a reference to the service, it can consume it (line 5).

Where can you inject dependencies this way? There are three general application components that are injected dependencies:

  • Controllers
  • Controller Filters
  • Custom Tags

The technique is exactly the same, just add @Inject annotation that requires a service from a Guice module, and you can use it in code inside the component

Mocking and testing

In testing, it is typical to replace real implementation of services with mocks. For explanation of mocks and stubs, follow this link http://martinfowler.com/articles/mocksArentStubs.html.

Why would someone want to use a mock instead of a real implementation? Here are some reasons:

  • Real implementation submits a sensitive transaction (you do not want that during a build!)
  • Real implementation requires a network connection to external resource
  • Real implementation is really... slow.
  • Real implementation does not always cover all conditions in your code at a given time
  • many others

when you use a real implementation of a service in tests, your test is not only testing your code, but also the implementation of a service, mushing everything together.

Now, lets create an mock service (this can also be created using any other mocking libraries, such as Mockito):

1
2
3
4
5
public class GreeterMock implements Greeter{
    public String greet() {
        return "Hello from " + this.getClass().toString();  
    }
}

and a new mock module we will use in tests:

1
2
3
4
5
6
public class GreeterMockModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Greeter.class).to(GreeterMock.class).asEagerSingleton();
    }
}

Once we have this done, we can inject the mock module during the test of a controller:

1
2
3
4
5
6
7
8
9
10
11
 public class HelloControllerSpec extends ControllerSpec {
     @Before
     public void before(){
         setInjector(Guice.createInjector(new GreeterMockModule()));
     }
     @Test
     public void shouldInjectMockService(){
         request().get("index");
         a(val("message")).shouldBeEqual("Hello from class app.services.GreeterMock");
     }
 }

Lets examine this test line by line:

  • Line 4 - this is where we tell the test scaffolding which module to use, and we chose a mock module
  • Line 8 - we construct a GET request for HelloController, action "index" and execute the controller.
  • Line 9 - we inspect that the controller did in fact send message to a view, but the value of this message will be generated by the mock service.

Please, refer to Kitchensink for a working example of DI in ActiveWeb.

Easier Mocking

While Google Guice provides a way of mocking by creating a new mock module, it feels heavy-handed. ActiveWeb specs provide a more elegant API for mocking:

1
2
3
4
5
6
@Test
public void shouldOverrideWithMock(){
    setInjector(createInjector(new GreeterModule()).override(Greeter.class).with(GreeterMock.class).create());
    request().get("index");
    a(responseContent()).shouldBeEqual("The greeting is: Hello from mock greeter");
}

You can chain the override(..).with(..) for multiple services in the container. Internally ActiveWeb simply creates dynamic modules on the fly.

Here, the GreeterMock.class will replace the real implementation of Greeter.class provided in the module (supposedly GreeterImpl.class).


How to comment

The comment section below is to discuss documentation on this page.

If you have an issue, or discover bug, please follow instructions on the Support page