Maintenance projects are not fun compared to greenfield projects, but they sure provide most of the meat for this blog. This week saw me not checking the production code but the tests. What you see in tests reveals much of how the production code itself is written. And it’s a way to change things for the better, with less risks.
At first, I only wanted to remove as much PowerMock uses as possible.
Then I found out most Mockito spies were not necessary.
Then I found out that Mockito mocks were initialized in 3 different ways in the same file; then usages of both given()
and when()
in the same class, then usages of when()
and doReturn()
in the same class…
And many more areas that could be improved.
In this post, I’ll limit myself to sum up the 3 ways to initialize your mocks in your test class: pick and choose the one you like best, but please stick with it in your class (if not your project). Consistency is one of the pillar of maintainability.
Reminder
Unit-testing is an important foundation of Software Quality (but not the only one!). As Object-Oriented Design is to make multiple components each with a dedicated responsibility, it’s crucial to make sure each of those components perform its tasks in a adequate manner. Hence, we have to feed a component with known inputs and check the validity of the outputs. Thus, testing the component in isolation requires a way to replace the dependencies with both input providers and output receivers that are in our control. Mockito is such a framework (among others) that let you achieve that.
Creating those mocks in the first place can be done using different ways (I’ll use Mockito wording in place of the standard one).
One-by-one explicit mocking
The first and most straightforward way is to use Mockito’s mock()
static method.
public class FooTest {
private Foo foo;
@Before
public void setUp() {
foo = Mockito.mock(Foo.class);
}
@Test
public void test_foo() {
Bar bar = Mockito.mock(Bar.class);
// Testing code go there
}
}
It might be the most verbose one, but it’s also the easiest to understand, as the API is quite explicit. Plus it’s not dependent on a testing framework. And you may mock On the downside, if you’ve got a high number of dependencies, you need to initialize them one by one - but perhaps having such a high number is a sign of bad design?
All-in-one general mocking call
The second option aims to fix this problem (the number of calls, not the design…).
It replaces all method calls by a single call that will mock every required attribute.
In order to tell which attribute should be a mock, the @Mock
annotation must be used.
public class FooTest {
@Mock private Foo foo;
@Mock private Bar bar;
@Before
public void setUp() {
MockitoAnnotations.initMocks();
}
@Test
public void test_foo() {
// Testing code go there
}
}
Here, the 2 Mockito.mock() calls (or more) were replaced by a single call.
On the downside, a local variable had to be moved to an attribute in order for Mockito to inject the mock.
Besides, you have to understand what how the initMocks()
method works.
JUnit runner
The last option replaces the explicit initMocks()
call with a JUnitRunner.
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock private Foo foo;
@Mock private Bar bar;
@Test
public void test_foo() {
// Testing code go there
}
}
This option alleviates the pain to write the single method call. But it has the downsides of option 2, plus it binds you to JUnit, foregoing other alternatives such as TestNG.
Conclusion
As a maintainer, my preferences are in decreasing order:
- Explicit method calls
-
because they’re the most explicit, thus readable
- initMocks
-
because it might hide the fact that you’ve too many dependencies… and having to replace local variables by attributes sucks
- Runner
-
for it’s even worse than the previous one. Didn’t you read all my claims about TestNG?
But the most important bit is, whatever option you choose, stick to it in the same class - if not the same project. Nothing makes code more unreadable than a lack of consistency.