While at MongoDB World 2016 I was involved in multiple conversations about unit testing and test driven development
of data access objects that wrap the MongoDB Java driver. Not two days after returning to work from MongoDB World
one of the teams I work with was complaining that the 3.x Java driver broke a lot of their testing patterns and that
they were having difficulties mocking out the driver. I had a few meetings canceled so I decided to sit down and
create a testing pattern that could be easily implemented and extended using the Mockito libraries.
A side note before we get started. If you are mocking at the driver layer you may want to take a step back and
consider why you are doing it and much of the following code violates the principle of 'Do no mock types you don't own'.
However, while I am not going to dive deep into the philosophy of unit testing, I think these testing patterns
may be of use to some. My general rule of
thumb is to only test logic, avoid the file system, and assume that the external systems work as advertised. Avoid
the pitfalls of only testing mocks for the sake of testing and make sure you set up integration layers to verify
that the external systems do, in fact, work as advertised. That being said, all the code for the following examples
may be found here.
Lets begin with setting up our base mocks and injecting them into the class we will be working on.
I am using the
Mockito's InjectMocks which will inject the mock MongoClient into the DriverWrapper class.
DriverWrapper is the class under test in this case. Using the Junit @Before annotation reduces the amount boilerplate
code needed in each test as this method will run before each test.
For the first test lets try something simple with a test for a method that finds documents by a last name field
And here is the method under test:
So this works, but the code feels pretty bulky. Following the red/green/refactor principles lets clean up the
implementation of the findByLastName method. Looking over the Java api there is a into method that will will replace
the body of the code above:
Well, that reduced the line count of the method by a bit... It did however break the test as we need to mock out
different parts of the code and needs to be updated to reflect the new implementation:
While the code has been reduced down to one line the test still has a lot of boilerplate that will need to be
"duplicated" in the next test. I am a fan of the builder pattern for problems like this so lets take a look at
a way to implement this pattern to create a more generic tool for setting up the tests.
Using this approach we are passing the mock MongoCollection we created in the test as a constructor parameter.
We then set collection as a field. We internalize the mock MongoCursor and FindIterable classes that we needed to
set up per test. Finally we use the builder pattern to define the mocking of the find and into methods. The test
may now be rewritten as:
Using this new builder it becomes much easier to create a test with multiple Documents returned by the query.
The main difference in this test is the use of the Hamcrest library's IsIterableContainingInOrder class. This
allows us to test the order of a returned list against an expected list in a direct assertion.
This concludes my basic implementation of using Mockito to test implementations of the MongoDB Java driver. I
hope you have found it useful. There are some other patterns and tests in the source code that I am working on
and my publish more on this later.
About Me
Jai Hirsch
Senior Systems Architect
CARFAX
jai.hirsch@gmail.com