I have started using Guice to do some dependency injection on a project, primarily because I need to inject mocks (using JMock currently) a layer away from the unit test, which makes manual injection very awkward.
My question is what is the best approach for introducing a mock? What I currently have is to make a new module in the unit test that satisfies the dependencies and bind them with a provider that looks like this:
public class JMockProvider<T> implements Provider<T> {
private T mock;
public JMockProvider(T mock) {
this.mock = mock;
}
public T get() {
return mock;
}
}
Passing the mock in the constructor, so a JMock setup might look like this:
final CommunicationQueue queue = context.mock(CommunicationQueue.class);
final TransactionRollBack trans = context.mock(TransactionRollBack.class);
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(CommunicationQueue.class).toProvider(new JMockProvider<QuickBooksCommunicationQueue>(queue));
bind(TransactionRollBack.class).toProvider(new JMockProvider<TransactionRollBack>(trans));
}
});
context.checking(new Expectations() {{
oneOf(queue).retrieve(with(any(int.class)));
will(returnValue(null));
never(trans);
}});
injector.getInstance(RunResponse.class).processResponseImpl(-1);
Is there a better way? I know that AtUnit attempts to address this problem, although I’m missing how it auto-magically injects a mock that was created locally like the above, but I’m looking for either a compelling reason why AtUnit is the right answer here (other than its ability to change DI and mocking frameworks around without changing tests) or if there is a better solution to doing it by hand.
You shouldn’t need to inject mocks though a DI framework. I’m using Guice and JMock quite successfully and my unit tests don’t reference anything Guice related. I only use mocks and pass in
nullwhere applicable.DI will allow the injection and construct of the dependencies of the current class, so If you want to add a mocked out class (which effectively stops the dependency graph) you just need to pass it in. Misko Hevery stated in one of the Google Tech Talks that unit tests should be littered with
new‘s andnull‘s because their scope is localized to the individual unit test method – I have to agree with him.Is there a reason to need to use Guice in the tests, that is if they are not functional / integration tests?
Wouldn’t the test work if it excluded the injector? Couldn’t you refactor your test to something like: