Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to use Guice and JMock together?

Tags:

java

guice

jmock

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.

like image 419
Yishai Avatar asked Jan 11 '10 21:01

Yishai


People also ask

How does dependency injection work Guice?

Using Guice In each of your constructors that need to have something injected in them, you just add an @Inject annotation and that tells Guice to do it's thing. Guice figures out how to give you an Emailer based on the type. If it's a simple object, it'll instantiate it and pass it in.

What does @inject do Guice?

Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.

Why is Guice useful?

The low level advantage of using Google Guice is a matter of cohesion in your application, your classes in the project can be loosely coupled between each other. I can provide a class for another class without them being dependent to each other.

What does bind mean in Guice?

A binding is an object that corresponds to an entry in the Guice map. You add new entries into the Guice map by creating bindings.


2 Answers

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 null where 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 and null'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:

final CommunicationQueue queue = context.mock(CommunicationQueue.class);
final TransactionRollBack trans = context.mock(TransactionRollBack.class);

context.checking(new Expectations() {{
    oneOf(queue).retrieve(with(any(int.class)));
    will(returnValue(null));
    never(trans);
}});

RunResponse r = new RunResponse(queue, trans); // depending on the order
r.processResponseImpl(-1);
like image 136
gpampara Avatar answered Oct 03 '22 21:10

gpampara


From what you said, it sounds like you are not doing a real unit-test. When doing unit-test for a class, you only focus on a single class. When you execute your unit-test, the execution should only exercise the subject class to be tested and avoid running methods from related classes.

JMock/Easymock are tools to help achieve unit-testing your class. They are not useful in integration test.

In the context of unit-testing, Guice is overlapped with JMock/Easymock. Guice forces users to pollute their code with all kind of annotations (such as @Inject) to achieve the same result as JMock/Easymock. I really strongly disagree with Guice in the way they help in testing. If developers find themselves to use Guice instead of JMock/Easymock, they properly either not doing a real unit-test or they try to test controller classes.

Testing controller class should not be done at unit-test level but at integration test level. For integration test, using Jakarta Cactus is much helpful. At integration test, developers should have all dependencies available and hence, there is no need to use Guice.

As a conclusion, I don't see any reasons to use Guice in testing a product. Google Guice might be useful in different contexts but testing.

like image 24
Thang Avatar answered Oct 03 '22 20:10

Thang