While mock objects are perfect instruments for unit testing, mocking through mock frameworks may turn your unit tests into an unmaintainable mess. Thanks to them we often hear that "mocking is bad" and "mocking is evil".
The root cause of this complexity is that our objects are too big. They have many methods and these methods return other objects, which also have methods. When we pass a mock version of such an object as a parameter, we should make sure that all of its methods return valid objects.
This leads to inevitable complexity, which turns unit tests to waste almost impossible to maintain.
Region interface from jcabi-dynamo as an example (this snippet and all others in this article are simplified, for the sake of brevity):
table() method returns an instance of the
Table interface, which has its own methods:
Frame, returned by the
frame() method, also has its own methods. And so on. In order to create a properly mocked instance of interface
Region, one would normally create a dozen other mock objects. With Mockito it will look like this:
And all of this is just a scaffolding before the actual testing.
Sample Use Case
Let's say, you're developing a project that uses jcabi-dynamo for managing data in DynamoDB. Your class may look similar to this:
You can imagine how difficult it will be to unit test this class, using Mockito, for example. First, we have to mock the
Region interface. Then, we have to mock a
Table interface and make sure it is returned by the
table() method. Then, we have to mock a
Frame interface, etc.
The unit test will be much longer than the class itself. Besides that, its real purpose, which is to test the retrieval of an employee's salary, will not be obvious to the reader.
Moreover, when we need to test a similar method of a similar class, we will need to restart this mocking from scratch. Again, multiple lines of code, which will look very similar to what we have already written.
The solution is to create fake classes and ship them together with real classes. This is what jcabi-dynamo is doing. Just look at its JavaDoc. There is a package called
com.jcabi.dynamo.mock that contains only fake classes, suitable only for unit testing.
Even though their sole purpose is to optimize unit testing, we ship them together with production code, in the same JAR package.
This is what a test will look like, when a fake class
MkRegion is used:
This test looks obvious to me. First, we create a fake DynamoDB region, which works on top of
H2Data storage (in-memory H2 database). The storage will be ready for a single
employees table with a hash key
name and a single
Then, we put a record into the table, with a hash
Jeff and a salary
Finally, we create an instance of class
Employee and check how it fetches the salary from DynamoDB.
I'm currently doing the same thing in almost every open source library I'm working with. I'm creating a collection of fake classes, that simplify testing inside the library and for its users.
BTW, a great article on the same subject: tl;dw: Stop mocking, start testing by Ned Batchelder.
PS. Check this out, on a very similar subject: Mocking of HTTP Server in Java.