Getting Started with Mockito
In this tutorial we will start our journey with Mockito, a full fledged Mocking framework in Java. As the article continues we will cover the following topics:
- Adding Mockito to Classpath
- Getting Started with Mockito for JUnit
- Getting Started with Mockito for TestNG
- Mockito best practices
Introduction
Mockito is an Open Source framework for Java that allows you to easily create test doubles or mocks.
Mockito is used to mock interfaces (behavior, not implementation) so that a dummy functionality can be added to a mock interface that can be used in unit testing. This article should help you learn how to create simple unit tests with Mockito as well as how to use its APIs in a simple and elegant manner.
On One hand, Mockito has a very active group of contributors and is actively maintained but on the other hand, the last Mockito release is version 1.9.5.
Mockito facilitates creating mock objects seamlessly. It uses Java Reflection in order to create mock objects for a given interface. Mock objects are nothing but proxy for actual implementations.
Adding to classpath, using Maven
The best way to add Mockito dependency to your project is using the Maven build system.
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
</dependency>
This dependency is simple enough and does not bring any additional or redundant libraries. See here for latest versions of the library.
We will also need a JUnit dependency. Let’s add it next,
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
Finally, we will also be using assertJ.
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.6.2</version>
<scope>test</scope>
</dependency>
Mockito and JUnit
We will get started with an example system under test straightaway. Our SUT is a MeanFactorCalculator, that uses an external service. Here is the system:
package com.discoversdk;
import com.discoversdk.model.User;
import com.discoversdk.service.SlabService;
public class MeanFactorCalculator {
private SlabService slabService;
public MeanFactorCalculator(SlabService slabService) {
this.slabService = slabService;
}
public double calculateMeanSlabFor(User user) {
double currentSlabFactor = slabService.getCurrentFactorFor(user);
double anotherSlabFactor = slabService.getCurrentFactorFor(user);
return (currentSlabFactor + anotherSlabFactor) / 2;
}
}
What to do next?
To use Mockito, we need to perform the following steps:
- Annotate the test class with @RunWith(MockitoJUnitRunner.class).
- Annotate the test fields with either @Mock or @Spy annotation to have either a mock or spy object instantiated.
- Annotate the system under test with @InjectMocks annotation.
Let’s apply the above three steps to provide our test demo.
package com.discoversdk;
import com.discoversdk.model.User;
import com.discoversdk.service.SlabService;
import static org.assertj.core.api.BDDAssertions.*;
import static org.mockito.BDDMockito.*;
import static org.mockito.Matchers.any;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class MockitoSample {
static final double TAX_FACTOR = 20;
@Mock
SlabService slabService;
@InjectMocks
MeanFactorCalculator systemUnderTest;
@Test
public void should_calculate_factor() {
//given given(slabService.getCurrentFactorFor(any(User.class))).willReturn(TAX_FACTOR);
// when
double meanFactor = systemUnderTest.calculateMeanSlabFor(new User());
// then
then(meanFactor).isEqualTo(TAX_FACTOR);
}
}
Sometimes, the test case classes have already been annotated with a @RunWith annotation and Mockito’s annotation won’t have any affect. In order to correct this, you have to call MockitoAnnotations.initMocks manually like:
@InjectMocks MeanFactorCalculator systemUnderTest;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
…
Terminology
Though we have already started with an excellent Test sample, it’s time to understand different object mocks which can be created using Mockito.
- Dummy: This is an object that is used only for code to compile and has no business logic associated with it, like a parameter passed to a function.
- Fake: This is an object that has an implementation but not ready for production, like H2 in-memory database.
- Stub: This is an object that has predefined answers to method executions made during the test.
- Mock: This is an object that has predefined answers to method executions made during the test and has recorded expectations of these executions.
- Spy: This is an object similar to stubs, but they additionally record how they were executed.
Mockito and TestNG
TestNG is a testing framework designed to cover all categories of tests: unit, functional, end-to-end, integration, etc., and it requires JDK 5 or higher.
TestNG is an open source automated testing framework where NG means Next Generation. TestNG is similar to JUnit but it is not a JUnit extension. It is inspired by JUnit. It is designed to be better than JUnit, especially when testing integrated classes.
Integration with Mockito
Some simple steps can be followed to integrate Mockito with TestNG. They are:
- Copy the MockitoAfterTestNGMethod, MockitoBeforeTestNGMethod and MockitoTestNGListener classes to your class.
- Annotate your class with @Listenere(MockitoTestNGListener.class).
- Annotate the test field with @Mock or @Spy annotation.
- Annotate the test fields with @InjectMocks annotation to instantiate it and then inject all @Mock or @Spy objects into it.
Let’s take a look at the code snippet we wrote before, again for the TestNG framework.
package com.discoversdk;
import com.discoversdk.model.User;
import com.discoversdk.service.SlabService;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import static org.assertj.core.api.BDDAssertions.then;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
@Listeners(MockitoTestNGListener.class)
public class MeanTaxFactorCalculatorTestNgTest {
static final double TAX_FACTOR = 10;
@Mock
SlabService slabService;
@InjectMocks
MeanFactorCalculator systemUnderTest;
@Test
public void should_calculate_factor() {
//given given(slabService.getCurrentFactorFor(any(User.class))).willReturn(TAX_FACTOR);
// when
double meanFactor = systemUnderTest.calculateMeanSlabFor(new User());
// then
then(meanFactor).isEqualTo(TAX_FACTOR);
}
}
For now, integration is easy. We will cover TestNG in details in a later section.
Best practices with Mockito
The common understanding of unit testing is testing the smallest possible part of the software, specifically a method. In reality, we do not test methods rather, we test a logical unit or the behavior of the system.
Readability
JUnit tests are written to test logical units. A test method name should portray the intention of the test so that a reader can understand what is being tested, such as the condition and the expectation or action.
Good test method names can be:
- should_not_register_a_null_user()
- should_throw_exception_when_a_null_user_is_registered()
Break Everything
An Extreme Programming concept is test everything that could possibly break. This means trying all different combinations of inputs to make sure we don't miss any combination that can cause the class to generate an error.
Ignore Trivial Tests
Writing trivial JUnits (such as those for getter and setter) is mostly a waste of time and money. We don't have the luxury to write infinite tests as it can eat our development time, application build time, and reduce test maintainability. If we start writing tests for getters and setters, we may miss more useful test cases.
Staying away from debugging
A common practice when we find a bug is to start debugging an application—stop doing this. Rather, add more tests to break the code; this will enrich your test suite and improve the system documentation.
So anyway, before starting to debug, create a (integration) test that reproduces the issue and then debug it. This will narrow down the problem, create a unit test for the lowest possible unit, and keep both the tests for future reference.
Check back soon more the next article in the series.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment