This is an AMP version of the article, its original content can be found here.
Built-in Fake Objects
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
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
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.