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:

and implementation of this interface:

We can then create a new Guice module:

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:

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:

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):

and a new mock module we will use in tests:

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

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:

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).

Easier Yet Mocking

In some cases you will not want to create a new module class for one or two services. There is an easier way to set your service interfaces and implementations without a new module.

Here is one example:

The framework will create a dynamic module, bind interfaces and implementations and will use it to create a new Injecotr on the fly.

Sometimes, you will only have a single service class not broken into an interface and implementation. In such a case, the to() method is optional:

The instance of a new injector will be added to the current context and will be used to inject services into filters and controllers executing by this test.

Do not forget to call a terminal method create()


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