TDD Best Practices: Don’t Mock Others

On April 1, 2012, in Uncategorized, by Derek Greer

Test Doubles play an important role in the practice of Test-Driven Development for establishing a controlled context, facilitating outside-in design, verifying component interaction, and providing overall test stability through isolation.  While isolating components from their dependencies is a Good Thing, some of the advantages of using test doubles and Test-Driven Development itself can be thwarted by substituting the wrong components.

One design principle which is particularly relevant to the practice of using test doubles (mocks, stubs, spies, fakes, etc.) is the Dependency Inversion Principle.  Stated simply, the Dependency Inversion Principle pertains to the decoupling of the stuff you most care about (like your domain layer) from the stuff you care least about (like your low-level infrastructure code and third-party libraries).  In relation to the practice of using test doubles, it’s the coupling to other people’s stuff that is most relevant.

When you provide test doubles for types you don’t own, this indicates that you have a Dependency Inversion Principle violation.  If you’re injecting concrete, abstract, or interface types for a third party dependency then that means your stuff can’t be used without their stuff.  While not ideal, you might ignore this design principle due your personal stance of: “They’ll pry framework XYZ from my cold, dead hands”.  Fair enough.  Nevertheless, there are a few other reasons why you might want to consider following this principle.

When using test doubles for third-party libraries, you make assumptions about how the third-party library works.  Perhaps the library you’re using has no bugs and you thoroughly understand all of its behavior, so you know that the behavior you are substituting behaves exactly like the real library will work in production once you put it all together.  If you aren’t confident of this, however, you might find that your design doesn’t interact with the third-party component in quite the way you imagined.  Let’s say it does though.  What about the next version?  When we design our systems using test doubles for third-party libraries, we create a false sense of security around the soundness and stability of our design.

Another problem caused by the use of test doubles for third-party components is the limitation it places on emergent design.  Coupling to third-party components will often lead to making design concessions to accommodate your dependencies rather than allowing your own design to emerge through the feedback received through the TDD process.

If we shouldn’t couple our design to third-party libraries (thereby removing the need to supply test doubles for third-party libraries), how then do we make use of such libraries and ensure our code works correctly?  The answer is to write component/unit level tests which guide the design of your own code which expresses its needs through its own interfaces (whose behavior is defined through the use of test doubles), and write integration tests which validate the behavior of adaptors which implement your design’s interfaces using the desired third-party components.

For example, you might design your components to rely upon an ILogger, IMapper, or IBus using test doubles to define their expected behavior and have integration tests which validate the implementation of these interfaces with log4net, AutoMapper, or ØMQ respectively.

In summary, don’t use test doubles for types you don’t own.  Instead, let any dependencies you take on be an outgrowth of your emergent design and provide integration tests to validate the expected behavior of any third-party libraries you use to facilitate the required behavior.

Tagged with:  
  • Rogerio

    These arguments make no sense to me.

    The DIP is just a technique to apply when you want to reuse a higher-level component/class with an open-ended set of different implementations of certain collaborators it depends on. Instead of depending on said collaborators directly, it depends on an abstract type defined at its own level. This, however, causes the lower-level implementations to now be dependent on that higher-level abstract type, making *them* less reusable. I use this technique only rarely, as it is simple not applicable to most situations. (IMO, naming it a “principle” was a poor choice.)

    Most units of code do use lower-level units without obeying the DIP, and this is perfectly fine in the vast majority of cases. For example, a Java business service class (the higher-level unit) using the Spring “MailSender” interface (the lower-level unit). To unit test the high-level business class, you would want to use a test double for the lower-level interface it uses, therefore mocking a type you don’t own. Applying the DIP in this case is possible (even easy), but would be very weird and pointless.

  • DLed

    a nice and simple explanation

  • DrOz

    Thanks for this interesting post.

    So when I have a wrapper for window.localStorage, I would then use the actual implementation in the (integration) tests for the wrapper instead of creating a mock?

    • derekgreer

      Yes, for a full end-to-end test you’d use the adaptor that implements the production implementation and you’d use test doubles for any programmer or component/unit level tests.

      You can basically think about it this way: If you upgrade to the latest version of framework XYZ which keeps the same interface but changes the behavior completely such that it breaks your application, all your isolation tests should be unaffected, but your integration tests should fail. The failed tests should tell you that you need to adjust your adaptors to the new framework (or switch to another framework), but the core of your application should remain unaffected.

      • DrOz

        OK, got it. Thanks a lot, Derek!