We have been bored to write so much boilerplate code for mocking dependencies in our unit tests. That’s why we have written MockInjector to automatically inject all mocks into our class under test.
Think of this class:
class MyClass {
@Inject Foo foo;
@Inject Bar foo;
void doSomething() {
foo.doSomething();
bar.doAnything();
}
}
If you want to test doSomething()
, you need mock Foo
and Bar
. The traditional way to do this with Mockito is:
class MyClassTest {
MyClass objectUnderTest;
Foo foo;
Bar bar;
@BeforeMethod public void setUp() {
foo = mock(Foo.class);
bar = mock(Bar.class);
objectUnderTest = new MyClass(foo, bar);
}
}
There is an other way, to use the mockito annotations:
class MyClassTest {
@InjectMocks MyClass objectUnderTest;
@Mock Foo foo;
@Mock Bar bar;
@BeforeMethod public void setUp() {
initMocks(this);
}
}
It’s always the same thing we do: Declare all dependencies, create mocks for them, inject the mocks into the class under test. We liked to do this in one statement. Here it is:
class MyClassTest {
MyClass objectUnderTest;
@BeforeMethod public void setUp() {
objectUnderTest = MockInjector.injectMocks(MyClass.class);
}
}
MockInjector.injectMocks()
finds all annotated dependencies and injects Mocks for them.
There are no dependency variables in the test class, which saves us two lines of code for each dependency.
But how do you stub and verify the interactions with the mocks? We just use package protected scope for our dependencies.
@Test
public void doSomething_calls_foo_and_bar() {
// when
objectUnderTest.doSomething();
// then
verify(objectUnderTest.foo).doSomething();
verify(objectUnderTest.bar).doAnything();
}
Discussion
You just reduced boilerplate code in the setup and added boilerplate code in test method ( verify(objectUnderTest.foo) instead of verify(foo) ) .
Good point, but there is one difference. The code in the test setup needs to be written manually but the added code in the test method is mostly written by IDE autocompletion.
Package protected scope is not really encapsulation. Anyone can mess up the dependencies by putting a class in the same package.
If you are afraid of other people breaking your encapsulation by working around package protected scope, you should not use MockInjector and package protected scope. In our team there is the convention to treat package protected fields like private fields. Since we usually do not share our code beyond our team, we have never experienced any problems of this kind. Package protected is private enough to tell: „Don’t change it unless you know what you do.“ If any team member wants to change something there he will not need to work around a package local scope. He can just edit the class itself and make public what he needs.
But there are more advantages:
- Think about the next iteration of
MyClass
: We need to add a new dependencybaz
. We start by writing a red unit test (compilation error):
@Test
public void doSomethingElse() {
given(objectUnderTest.baz)
}
Now we can use the quickfix feature of our IDE to introduce the new field and then add the
@Inject
annotation. No need to modify the test setup. Everything works out of the box.We often refactor our code to meet new requirements. That includes the renaming of classes and fields. In former times we often forgot to rename the variables in the test setup. Then it was hard to understand, what the test really did, until you realize that you just needed to rename the variable of the mock. Because there is no field for the mock anymore we do not need to rename it 🙂
If we forget to annotate any dependency MockInjector will not mock it and the test will fail. No surprises with NullPointerExceptions on production.
Is it production ready?
We have been successfully working with MockInjector for several years. Besides that it’s not production, it’s test code. If MockInjector makes your red tests green, it obviously works.
Do I need a special test or dependency injection framework to use it?
No. There is a dependency to javax.inject.Inject but you can use it with Spring Framework or Google Guice, too. You can use it with any test framework you want.
Where can I get it?
MockInjector is available on https://github.com/hypoport/MockInjector
Package protected fields in combination with MockInjector for testing is the easiest and most straight forward way of coding we found. Give it a try!
One more thing for Intellij IDEA users:
With this file template for new class files you do not even need to write „injectMocks“ manually.
#parse("File Header.java")
#if (${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#if ($NAME.endsWith("Test"))
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.hypoport.mockito.MockInjector.injectMocks;
#end
#parse("Type Header.java")
public class ${NAME} {
#if ($NAME.endsWith("Test"))
$NAME.replace("Test", "") $NAME.substring(0, 1).toLowerCase()$NAME.replace("Test", "").substring(1);
@BeforeMethod
public void setUp() throws Exception {
$NAME.substring(0, 1).toLowerCase()$NAME.replace("Test", "").substring(1) = injectMocks($NAME.replace("Test", "") .class);
}
#end
}