
MOCKITO COOKBOOK
By :

Before going into details regarding Mockito and JUnit integration, it is worth mentioning a few words about JUnit.
JUnit is a testing framework (an implementation of the xUnit framework) that allows you to create repeatable tests in a very readable manner. In fact, JUnit is a port of Smalltalk's SUnit (both the frameworks were originally implemented by Kent Beck). What is important in terms of JUnit and Mockito integration is that under the hood, JUnit uses a test runner to run its tests (from xUnit—test runner is a program that executes the test logic and reports the test results).
Mockito has its own test runner implementation that allows you to reduce boilerplate in order to create test doubles (mocks and spies) and to inject them (either via constructors, setters, or reflection) into the defined object. What's more, you can easily create argument captors. All of this is feasible by means of proper annotations as follows:
@Mock
: This is used for mock creation@Spy
: This is used to create a spy instance@InjectMocks
: This is used to instantiate the @InjectMock
annotated field and inject all the @Mock
or @Spy
annotated fields into it (if applicable)@Captor
: This is used to create an argument captorBy default, you should profit from Mockito's annotations to make your code look neat and to reduce the boilerplate code in your application.
In order to add JUnit to your classpath, if you are using a dependency manager that connects to the Maven Central Repository, then you can get your dependencies as follows (examples for Maven and Gradle):
To add JUnit in Maven, use the following code:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
To add JUnit in Gradle, use the following code:
testCompile('junit:junit:4.11')
If you are not using any of the dependency managers, you have to download the following jars:
junit.jar
hamcrest-core.jar
Add the downloaded files to your classpath manually (you can download the jars from https://github.com/junit-team/junit/wiki/Download-and-Install).
For this recipe, our system under test will be a MeanTaxFactorCalculator
class that will call an external service, TaxService
, to get the current tax factor for the current user. It's a tax factor and not tax as such, since for simplicity, we will not be using BigDecimals
but doubles
, and I'd never suggest using doubles
to anything related to money, as follows:
public class MeanTaxFactorCalculator { private final TaxService taxService; public MeanTaxFactorCalculator(TaxService taxService) { this.taxService = taxService; } public double calculateMeanTaxFactorFor(Person person) { double currentTaxFactor = taxService.getCurrentTaxFactorFor(person); double anotherTaxFactor = taxService.getCurrentTaxFactorFor(person); return (currentTaxFactor + anotherTaxFactor) / 2; } }
To use Mockito's annotations, you have to perform the following steps:
@RunWith(MockitoJUnitRunner.class)
.@Mock
or @Spy
annotation to have either a mock or spy object instantiated.@InjectMocks
annotation to first instantiate the @InjectMock
annotated field and then inject all the @Mock
or @Spy
annotated fields into it (if applicable).@Captor
annotation to make Mockito instantiate an argument captor (refer to Chapter 6, Verifying Test Doubles, for more details).The following snippet shows the JUnit and Mockito integration in a test class that verifies the SUT's behavior (remember that I'm using BDDMockito.given(...)
and AssertJ's BDDAssertions.then(...)
static methods; refer to Chapter 7, Verifying Behavior with Object Matchers, for how to work with AssertJ or how to do the same with Hamcrest's assertThat(...)
method):
@RunWith(MockitoJUnitRunner.class) public class MeanTaxFactorCalculatorTest { static final double TAX_FACTOR = 10; @Mock TaxService taxService; @InjectMocks MeanTaxFactorCalculator systemUnderTest; @Test public void should_calculate_mean_tax_factor() { // given given(taxService.getCurrentTaxFactorFor(any(Person.class))).willReturn(TAX_FACTOR); // when double meanTaxFactor = systemUnderTest.calculateMeanTaxFactorFor(new Person()); // then then(meanTaxFactor).isEqualTo(TAX_FACTOR); } }
To profit from Mockito's annotations using JUnit, you just have to annotate your test class with @RunWith(MockitoJUnitRunner.class)
.
The Mockito test runner will adapt its strategy depending on the version of JUnit. If there exists a org.junit.runners.BlockJUnit4ClassRunner
class, it means that the codebase is using at least JUnit in Version 4.5.What eventually happens is that the MockitoAnnotations.initMocks(...)
method is executed for the given test, which initializes all the Mockito annotations (for more information, check the subsequent There's more… section).
You may have a situation where your test class has already been annotated with a @RunWith
annotation and, seemingly, you may not profit from Mockito's annotations. In order to achieve this, you have to call the MockitoAnnotations.initMocks
method manually in the @Before
annotated method of your test, as shown in the following code:
public class MeanTaxFactorCalculatorTest { static final double TAX_FACTOR = 10; @Mock TaxService taxService; @InjectMocks MeanTaxFactorCalculator systemUnderTest; @Before public void setup() { MockitoAnnotations.initMocks(this); } @Test public void should_calculate_mean_tax_factor() { // given given(taxService.getCurrentTaxFactorFor(Mockito.any(Person.class))).willReturn(TAX_FACTOR); // when double meanTaxFactor = systemUnderTest.calculateMeanTaxFactorFor(new Person()); // then then(meanTaxFactor).isEqualTo(TAX_FACTOR); } }
To use Mockito's annotations without a JUnit test runner, you have to call the MockitoAnnotations.initMocks
method and pass the test class as its parameter.
Mockito checks whether the user has overridden the global configuration of AnnotationEngine
and, if this is not the case, the InjectingAnnotationEngine
implementation is used to process annotations in tests. What is done internally is that the test class fields are scanned for annotations and proper test doubles are initialized and injected into the @InjectMocks
annotated object (either by a constructor, property setter, or field injection, in that precise order).
You have to remember several factors related to the automatic injection of test doubles as follows:
@InjectMocks
annotated fields through either of the strategies, it won't report failure—the test will continue as if nothing happened (and most likely, you will get NullPointerException
).@InjectMocks
annotated object wasn't previously initialized, then Mockito will instantiate the aforementioned object using a no-arg constructor if applicable.@InjectMocks
Mockito documentation (with description of injection strategies) at http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/InjectMocks.htmlChange the font size
Change margin width
Change background colour