Lightweight Testing with Custom Guice Injectors

Léo Grimaldi
Keep It Up
Published in
2 min readAug 1, 2013

Implementation details on GitHub.

In our last post, I introduced the Configuration vs Functional Module pattern that we use to organize our Guice dependencies. This pattern is implemented in a way that forces us to maintain small functional modules. This approach increases flexibility in testing as these small modules can be combined on the spot to create lightweight testing environments with custom Guice injectors.

Guice injectors were already mentioned as an option to avoid the overhead of loading a new Play! application in our post on how to Speed up your Play! controller tests 100x. Let’s have another look.

We try to write simple tests with no Play! application and no injection whenever possible:

However, we occasionally need a more complex testing environment, with a few injected classes, access to the database or even a running application. We have different testing scenarios covering these requirements. For each scenario, we have helper traits that enable us to use Guice bindings in our tests.

Create a custom Guice Injector outside of a Play! Application:

  • Mix TestInjector trait into Specification
  • Use withInjector method

We don’t need a running Play! application to test Marvin’s reaction to the number of users aboard our remote Spaceship service. We can simply inject a FakeSpaceshipClient on the spot:

We hope that Marvin’s depression will get better, so that we can get this test to pass over the next few million years.

Run a Play! Application with a custom Guice injector:

  • Mix ApplicationInjector trait into Specification
  • Use TestApplication class

In order to test the SpaceshipClient itself, we need a running Play! application to handle the routing with an injected FakeHttpClient:

Starting a Play! application makes this test slower than the previous one, though not a slow as an actual HTTP request to our remote Spaceship.

The traits TestInjector and ApplicationInjector both extend EmptyInjector and inherit the inject[MyClass] method that retrieves an instance of MyClass from an implicit Guice Injector in scope.

TestInjector enables us to create such an injector on the spot from Guice modules, using the withInjector method.

ApplicationInjector will retrieve the injector of the running application in scope, which we create in our GlobalSettings by having it extend EmptyInjector in the first place. ApplicationInjector is only provided for convenience: should we need to mix tests with and without running applications, we can use TestInjector and retrieve the application injector manually when a test needs it.

The fewest possible default functional modules are provided. This is an incentive for us to keep test dependencies minimal and explicit. Finally, other test scenarios may involve more complex subclasses of EmptyInjector that will wrap up the withInjector method with additional behavior, such as starting and shutting down a database outside of a Play! application.

Originally published at eng.kifi.com on August 1, 2013.

--

--

Léo Grimaldi
Keep It Up

Engineering @YouTube. Former French Pastry Officer @Kifi @Stanford @CentraleParis