4

I've been learning about Dependency Injection (e.g. Guice) and it seems to me one of the main drivers, testability, is covered already quite nicely by Mocking (e.g. Mockito). Difference between Dependency Injection and Mocking framework (Ninject vs RhinoMock or Moq) is a nice summary of commonality between Dependency Injection and Mockito but it doesn't offer guidance on which to use when they overlap in capability.

I'm about to design an API and I'm wondering if I should:

A] Use Mockito only

B] Use Guice and design two implementation of interfaces--one for real and one for testing

C] Use Mockito AND Guice together--if so, how?

I'm guessing the right answer is C, to use them both, but I'd like some words of wisdom: where can I use either Dependency Injection or Mocking, which should I choose and why?

Michael Osofsky
  • 11,429
  • 16
  • 68
  • 113
  • 1
    You can use both together in unit tests, and dependency injection alone in production code. In unit tests, you can use dependency injection to inject mocks. – Andy Thomas Dec 02 '14 at 23:24
  • Thanks @AndyThomas, what is the advantage of injecting mocks? It seems like less code just to call `mock()` on my class and then call `stub()` to override behavior. With dependency injection won't I have to write an implementation of `Module` and then write a class that implements the whole interface of the class I want to test? Seems like a lot of code. I'm sure there's no "right answer" but I'd appreciate words of wisdom. – Michael Osofsky Dec 02 '14 at 23:56
  • 1
    You can do that too, with non-final classes and methods. Sometimes designing for dependency injection has benefits beyond unit testing, in which cases you can exploit it during unit testing. – Andy Thomas Dec 03 '14 at 00:13
  • "Use Guice and design two implementation of interfaces--one for real and one for testing". One issue here is that you may need dozens of (unit-test) implementations for testing your code. While the simple unit-test-implementation may be to just "do nothing" in your void methods...or return a default object in getmethods...(etc, etc)...........you may need the unit-test implementation to do different things. like throw an exception......or return a null item from a get method......this is why you need a mock framework...it makes coding up these dozens of unit-test situations.....easier. – granadaCoder Jun 13 '18 at 14:18

2 Answers2

19

Guice and Mockito have very different and complementary roles, and I'd argue that they work best together.

Consider this contrived example class:

public class CarController {
  private final Tires tires = new Tires();
  private final Wheels wheels = new Wheels(tires);
  private final Engine engine = new Engine(wheels);
  private Logger engineLogger;

  public Logger start() {
    engineLogger = new EngineLogger(engine, new ServerLogOutput());
    engine.start();
    engineLogger.recordEvent(ENGINE_STARTED);
    return engineLogger;
  }
}

Notice how much extra work this class does: You don't actually use your Tires or Wheels other than to create a working Engine, and there's no way to substitute your Tires or Wheels: Any car, in production or in test, must have real Tires, real Wheels, a real Engine, and a real Logger that really logs to a server. Which part do you write first?

Let's make this class DI-friendly:

public class CarController { /* with injection */
  private final Engine engine;
  private final Provider<Logger> loggerProvider;
  private Logger engineLogger;

  /** With Guice, you can often keep the constructor package-private. */
  @Inject public Car(Engine engine, Provider<Logger> loggerProvider) {
    this.engine = engine;
    this.loggerProvider = loggerProvider
  }

  public Logger start() {
    engineLogger = loggerProvider.get();
    engine.start();
    engineLogger.recordEvent(ENGINE_STARTED);
    return engineLogger;
  }
}

Now the CarController doesn't have to concern itself with the tires, wheels, engine, or log output, and you can substitute in whichever Engine and Logger you'd like by passing them into the constructor. In this way, DI is useful in production: With the change of a single module, you can switch your Logger to log to a circular buffer or local file, or switch to a supercharged Engine, or upgrade to SnowTires or RacingTires separately. This also makes the class more testable, because now substituting out implementations becomes much easier: you can write your own test doubles such as FakeEngine and DummyLogger and put them in your CarControllerTest. (Of course, you can also create setter methods or alternate constructors, and you can design the class in this way without actually using Guice. Guice's power comes from constructing large dependency graphs in a loosely-coupled way.)

Now, for those test doubles: In a world with only Guice but no Mockito, you would have to write your own Logger-compatible test double and your own Engine-compatible test double:

public class FakeEngine implements Engine {
  RuntimeException exceptionToThrow = null;
  int callsToStart = 0;
  Logger returnLogger = null;

  @Override public Logger start() {
    if (exceptionToThrow != null) throw exceptionToThrow;
    callsToStart += 1;
    return returnLogger;
  }
}

With Mockito, that becomes automatic, with better stack traces and many more features:

@Mock Engine mockEngine;
// To verify:
verify(mockEngine).start();
// Or stub:
doThrow(new RuntimeException()).when(mockEngine).start();

...and that's why they work so well together. Dependency injection gives you the chance to write a component (CarController) without concerning yourself with its dependencies' dependencies (Tires, Wheels, ServerLogOutput), and to change dependency implementations at your will. Then Mockito lets you create these replacement implementations with minimal boilerplate, which can be injected wherever and however you'd like.

Side note: Neither Guice nor Mockito should be a part of your API, as you mention in the question. Guice can be a part of your implementation details, and may be a part of your constructor strategy; Mockito is a part of your testing and shouldn't have any effect on your public interface. Nevertheless, the choice of frameworks for OO design and testing is an excellent discussion to have before starting your implementation.


Update, incorporating comments:

  • Typically, you won't actually use Guice in unit tests; you'll call the @Inject constructors manually with the assortment of objects and test doubles you prefer. Remember that it's easier and cleaner to test state rather than interactions, so you'll never want to mock data objects, you'll almost always want to mock remote or asynchronous services, and that expensive and stateful objects might be better represented with lightweight fakes. Don't be tempted to over-use Mockito as the only solution.

  • Mockito has its own "dependency injection" feature called @InjectMocks, which will replace the system-under-test's fields with @Mock fields of the same name/type even if there are no setters. This is a nice trick to replace dependencies with mocks, but as you noted and linked, it will fail silently if dependencies are added. Given that downside, and given that it misses out on much of the design flexibility that DI provides, I've never had a need to use it.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • 1
    Well said... what I wanted to write but didn't have the words for. :-) – The111 Dec 03 '14 at 08:13
  • 1
    Thanks @JeffBowman, that's a super detailed answer. Thanks for your comment about neither Guice nor Mockito being exposed as a part of my API. I'll hide them then. – Michael Osofsky Dec 03 '14 at 17:35
  • Thanks again @JeffBowman, I'm sold on the benefits of Dependency Injection for clean design. But for testing, I'm still confused. When given a choice to call Mockito's mock() and stub() or use Guice to inject Test Doubles, which way would you go and why? – Michael Osofsky Dec 03 '14 at 17:37
  • 1
    If the question is whether to use Mockito versus other test doubles, that's a judgment call that depends on how much state the mocked object represents: You should never mock data objects, always mock remote services, and consider fakes for expensive stateful objects. Read [the linked article](http://martinfowler.com/articles/mocksArentStubs.html) for more guidance. Guice itself probably has no place in your unit tests, but after designing your component for DI, you can call those useful @Inject constructors with whichever test doubles (or real implementations) you'd like. – Jeff Bowman Dec 03 '14 at 18:44
  • Excellent answer. Minor nit: I'd avoid making the `@Inject`ed constructor `public`. – Tavian Barnes Dec 03 '14 at 20:35
  • 1
    A good article [Mockito: Why You Should Not Use InjectMocks Annotation to Autowire Fields](http://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/) explains that Mockito's Dependency Injection tag, `@InjectMocks` can silently fail if you add more dependencies to the class under test. Explicit dependency injection with Spring is recommended (I assume Guice is a suitable alternative to Spring). – Michael Osofsky Dec 03 '14 at 23:00
  • You are a god amongst mere mortals. Thank you. :) – null May 29 '15 at 19:23
3

Take a look at Jukito, its a mix of Mockito and Guice and Junit.

https://github.com/ArcBees/Jukito

http://jukito.arcbees.com/

echen
  • 75
  • 1
  • 10