Pages

Thursday, 19 November 2015

Stop Mocking Me!

Mocking (and other test doubles) are very powerful tools that every developer should know. But more important than knowing how to use them is knowing when to use them.

First lets explore the different types of test doubles:

Dummy objects are empty shells that get passed about but do nothing.
Fake objects re-implement the functionality but in a test-friendly way, for example an in-memory database.
Stub objects provide canned responses and optionally high level verification.
Mock objects provide canned responses with method-level verification.
Tear-down-ables provide mini environments for your tests, in the JUnit world these are often rules ranging from the file systemmini hadoop cluster all the way up to complete systems. This one I find rarely talked about.

Mocking frameworks (like EasyMock or Mockito) can provide dummies, stubs and mocks.  The names of all of them often get mixed up but for this article I'll use the above (which match Martin Fowlers).

When to use none of them

  • an exception (to create throw the exception, sure but not for the exception itself) 
  • DTOs 
  • Fast, tested objects with very few in-app dependencies and no external dependencies, for example domain objects.  
  • External APIs that don't hit external resources.

With the exception of Fakes and Tear-down-ables, every time you add a test double you tightly link the implementation of the system under test to the test method. What you should be attempting to do is link the result of the system under test to the test.
For the above three you should prefer to use the real object rather than a test double.

Dummies
Are rarely used in my experience. As a general rule if nothing is going to happen to an object then pass null or a (if easy to set up) real object.

Fakes
These require more work to create and can be slower than using other test doubles, but can make tests significantly simpler to follow.
They test the results of an action, and not the interactions between objects. Often this is acceptable but when you require an edge case (such as throwing an exception) it is better to use a stub or mock.

Stubs
Stubs are useful when you have simple object interactions that you want to make some general verification on the value of the parameters passed about.

Mocks
Pure mocks should be avoided except when the exact interaction between two objects needs to be verified. This is rare and stubs are generally what are required.

Tear-down-ables 
Tear-down-ables arguable move you from unit testing to integration testing, so you should be aware of the additional overhead involved. However in the right case, they can significantly simplify your test as you are using the real(ish) dependency. If you class writes a file to the file system then mocking or stubbing FileWriter and friends offers no value, seeing a file on the file system does.

Warning signs
When working with test doubles the test will often give you a warning if you are using the wrong type of test double.

  • If you look at your test and you can't work out what you are testing then look at the test doubles you are using. 
  • Are you re-writing the method in your test when using a mock or stub then that is a sign you should be moving towards either fakes or tear-down-ables.
  • Are you setting up a large dependency tree? Maybe some of that should be stubbed out.

No comments:

Post a Comment