If you’ve ever been involved in a major effort to go from 0% code coverage to the coveted 80%, you know that it’s no easy task. The reason is due not only to the sheer number of staff hours but also to the fact that, if your code is anything like mine, your code may reflect bad practices. (For the record, I believe that you should aim for as much coverage as is necessary to comfortably release your code — not necessarily 80%.)
I expect a high level of coverage. Sometimes managers require [a high level of coverage]. There's a subtle difference.
— Brian Marick
If you ever find yourself desperately trying to increase your code coverage, I would recommend devoting just a small, agreed upon percentage of sprint points to cleaning up technical debt (essentially, any debt you owe to your code for taking shortcuts along the way). That means not unit testing, not documenting, not fixing code smells, and a slew of other things. In order for this to work, everyone would have to follow the old boy scout rule, “I will not increase the technical debt.” Slowly but surely, you will see a difference. (Be on the lookout for a future blog post about managing technical debt.)
We recently had to start writing unit tests on a project with 0% code coverage. Below, I’ll talk about some of the challenges we faced — challenges many others are likely to encounter themselves.
Before doing a deeper dive into mocking, let’s start with some terminology you’re likely to come across.
Mocks vs. Stubs
It took me some time to grasp the difference between mocks and stubs. This helpful stackoverflow page provides a starting point for understanding the difference. Also, here are Martin Fowler’s definitions of some important terms:
- Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
- Fake objects actually have working implementations but usually take some shortcut, which makes them unsuitable for production (an in memory database is a good example).
- Stubs provide canned answers to calls made during the test and usually don’t respond at all to anything outside what's programmed in for the test.
- Spies are stubs that also record some information based on how they were called. One form might be an email service that records how many messages were sent.
- Mocks are objects pre-programmed with expectations that form a specification of the calls they are expected to receive.
While Fowler’s definitions are useful, specifically for more advanced developers, there’s still a lot of debate around these terms. Let me make this a bit simpler. All of these terms, at the end of the day, are just mocks. In other words, they are just fake classes. Whether or not we choose to interact with the mock to check for behavior doesn’t make it any less of a mock.
I’ll give you an example for the two terms that gave me the most trouble, mocks and stubs.
An example of a stub would be having a class that sends an alert. Instead of sending the alert, we can stub that functionality out and return some other dummy values.
With a mock, on the other hand, we care about behavior. For example, when we call the bakeCake() method, we are confirming that we are in turn calling the mixIngredients() and preheatOven() methods.
Mocking Protected Methods
Let’s jump right into mocking. Here are a couple of Mockito scenarios we had to work around.
Oftentimes you will find yourself dealing with protected and private methods. How can you override the return of a protected method from your test? When testing a method with an underlying call to a protected method that you’d like to mock, one easy solution is to simply create a class extending the mocked class.
The class to test:
The extended class:
And the test:
You can see from the code above that now we are actually creating a new class called MockClass that extends UnderTest. We have access to override any underlying method within that class (and any subsequent classes). Using the When/Then notation, I can force the call to one of my methods to call the real override method that was created. You can also override this value too, as in this example:
Here is a basic model of what we are doing:
Web MVC Testing
Now we need a way to test a regular spring controller. We have a basic app that has a rest controller (PostController) for posting links to a page.
The PostController makes reference to a basic @Repository and a post class containing fields for a name, title, link, etc.
Here’s how we test it:
Using MockMvc, we are able to perform get and post calls, pass in parameters, print out the result of the call, and verify that we get our expected 200 OK status.
Mocking Static Methods
Let me set the context here. So now you want to override the result of a static method call. But that’s not how mocking works. A mock, being a mock of a class, needs a real instance of an object.
I’m gonna go ahead and spare you. Mocking static methods as well as private methods is possible using a library called PowerMock, but the Java overlords will forever shun you for using it. The only reason PowerMock works is because it gives you the ability to mock static and private methods by manipulating the underlying bytecode. However, there is still a way to do it all with Mockito by implementing a solution similar to the first one using a wrapper method and a spy.
Spies allow us to partially mock. This means that we can mock a part of the object but allow for real method calls for the rest of the object.
The class to test:
Here, we are creating a wrapper method that calls the static method. Now, in our regularMethod, we can call this wrapper class instead of calling the static class directly. A spy does exactly what it sounds like -- it allows us to spy on real objects. In other words, using a spy (our partial mock) enables us to override what the method returns.
Some people will tell you that you should avoid static methods at all costs since they usually lead to bad practices. I’m going to avoid that debate all together simply because, even if you have no static methods in your project whatsoever, you might still find yourself mocking a dependency that you never wrote. You can’t really control the libraries you depend on.
I’m far from a Java expert let alone a unit testing expert, but I hope that the information on mocking provided here helps you with the more challenging aspects of unit testing. Please don’t hesitate to reach out if you have any questions or comments!