So I've been trying to figure this out on my own via documentation but I'm not getting anywhere.
I've got some simple DI bindings setup in a service class that creates a repository object. Simple. However, when I run this in test mode, @Inject does nothing and the repository object is never instantiated.
@Inject
TagRepository tagRepository;
So on the line where it's use, in test mode, we of course get a NullPointerException
tagRepository.tagExistsByName(tag);
This bubbles up into my test like so:
[error] Test services.TagsServiceTest.testAddNewTag failed: java.lang.NullPointerException: null, took 0.097 sec
[error] at services.TagService.tagExists(TagService.java:27)
[error] at services.TagService.addNewTag(TagService.java:18)
[error] at services.TagsServiceTest.testAddNewTag(TagsServiceTest.java:29)
My question is, how do I configure my application to use Guice injectors in test mode? I didn't have this problem with my controllers because requests were actually being made to them, setting up the full application.
One thing I should mention is that I'm using a provider to provide my app to the tests. Should I be using the Guice application builder? If so, where does that go? The play docs aren't very helpful in that regard. Here is the provider
@Override
protected FakeApplication provideFakeApplication() {
return new FakeApplication(new java.io.File("."), Helpers.class.getClassLoader(), ImmutableMap.of("play.http.router", "router.Routes"), new ArrayList<String>(), null);
}
UPDATE:
Here is the update based on the suggestion below
Inside my BaseTest class
@Override
protected Application provideApplication() {
return new GuiceApplicationBuilder().in(Mode.TEST).build();
}
And then in the service testing class
@Before
public void beforeTest() {
Injector injector = new GuiceInjectorBuilder().bindings(bind(TagService.class).toInstance(new TagService())).injector();
tagService = injector.instanceOf(TagService.class);
}
However, I'm still getting null pointer exceptions because the TagRepository isn't being injected.
ANSWER:
I was thinking about this slightly wrong. If you setup the injector with the object you need to inject, then create an instance from that, you won't get any more NullPointerExceptions
@Before
public void beforeTest() {
Injector injector = new GuiceInjectorBuilder().bindings(bind(TagRepository.class).toInstance(new TagRepository())).injector();
tagService = injector.instanceOf(TagService.class);
}
If you're extending WithApplication
, you can override protected Application provideApplication()
to return an Application
built using GuiceApplicationBuilder
.
For example (based on this code) you can create your app, add or override bindings, etc, and set the mode. If you make the class abstract, it will then work automatically with all child classes.
public abstract class AbstractFakeApplicationTest extends WithApplication
{
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractFakeApplicationTest.class);
@Override
protected Application provideApplication()
{
return new GuiceApplicationBuilder().in(Mode.TEST)
.build();
}
@Override
public void startPlay()
{
super.startPlay();
// mock or otherwise provide a context
Http.Context.current.set(new Http.Context(1L,
Mockito.mock(RequestHeader.class),
Mockito.mock(Http.Request.class),
Collections.<String, String>emptyMap(),
Collections.<String, String>emptyMap(),
Collections.<String, Object>emptyMap()));
}
public Http.Context context()
{
return Http.Context.current.get();
}
}
Child classes then just extend this class and test as normal - all DI should occur as it does when you run the app normally.
You can see various examples of it here
This gives the basic outline for what you need to do. Hopefully the docs at https://playframework.com/documentation/2.4.x/JavaTestingWithGuice will make a bit more sense now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With