How to Use Mockito in Android
Learn how to integrate Mockito with your app and use the mock and spy methods to unit test your Android application.
Join the DZone community and get the full member experience.
Join For FreeUnit testing is an important part of any product development lifecycle. The main purpose of unit testing is to test components in isolation from each other and that is how our code should be written as well. To achieve this task of writing test cases for a class in isolation from the others, there are times when we need to mock some objects or data while writing our JUnit test cases. As the name Mockito suggests, it is a framework that allows us to do just that.
If the UI related part of our code is already tested by some testing framework like Espresso, then it need not be tested again. Also, we do not need to test code that relies on the Android OS. So, we can use Mockito to test our non-UI or functional code that is not dependent on the Android OS.
By default, when we run our local unit test cases, they run against a modified version of the android.jar file which does not contain any actual code because of which, when we try to invoke any method of an Android class from our test case, we get an exception. This is done to ensure that we test only our code and do not depend on some default behavior of the Android classes. We can also change this default behavior of throwing an exception to return zero or null values. However, we should avoid this as it could cause regressions in our test cases which are hard to debug. If we still wish to do this, it can be done by including the following in our project’s top-level build.gradle file:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}
}
We can substitute Android dependencies with mock objects. We can use Mockito for this to configure our mock objects to return some specific value when they are invoked.
Mockito Integration
To integrate Mockito in our Android apps, we first need to include the following dependencies in our app level build.gradle file.
dependencies {
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
}
Once we have added the required dependencies, we will create our test class in the directory module-name/src/test/java/, and add the @RunWith(MockitoJUnitRunner.class) annotation at the beginning of this class. This annotation tells the Mockito test runner to validate if the usage of the framework is correct and it also simplifies the mock object initializations.
We have to add @Mock annotation before the field declaration of any object we want to mock.
We can use the when() and thenReturn() methods to return a value when a particular condition is met in order to stub the behavior of any dependency.
Now, let’s have a look at a sample test class. The sample below checks that the method getHelloWorldString() of the class ClassUnderTest matches a given string.
public class ClassUnderTest {
Context mContext;
public ClassUnderTest(Context context) {
mContext = context;
}
public String getHelloWorldString() {
return mContext.getString(R.string.text_hello_word);
}
}
public class ExampleUnitTest {
private static final String TEST_STRING = "HELLO WORLD!";
//As we don't have access to Context in our JUnit test classes, we need to mock it
@Mock
Context mMockContext;
@Test
public void readStringFromContext() {
//Returns the TEST_STRING when getString(R.string.hello_world) is called
when(mMockContext.getString(R.string.text_hello_word)).thenReturn(TEST_STRING);
//Creates an object of the ClassUnderTest with the mock context
ClassUnderTest objectUnderTest = new ClassUnderTest(mMockContext);
//Stores the return value of getHelloWorldString() in result
String result = objectUnderTest.getHelloWorldString();
//Asserts that result is the value of TEST_STRING
assertThat(result, is(TEST_STRING));
}
}
How to Mock
We have two methods, mock() and spy(), that can be used to create mock methods or fields.
Using the mock() method, we create complete mock or fake objects. The default behavior of mock methods is to do nothing.
Using the spy() method, we just spy or stub specific methods of a real object. As we use real objects in this case, if we do not stub the method, then the actual method is called.
We generally use mock() when we need complete mock objects, and use spy() when we need partial mock objects for which we need to mock or stub only certain methods.
Let’s see how we can use mock() and spy() methods:
@Test
public void testMockMethod(){
List mockList = Mockito.mock(ArrayList.class);
mockList.add("hello world");
Mockito.verify(mockList).add("hello world");
assertEquals(0, mockList.size());
}
@Test
public void testSpyMethod(){
List spyList = Mockito.spy(new ArrayList());
spyList.add("hello world");
Mockito.verify(spyList).add("hello world");
assertEquals(1, spyList.size());
}
In the above example, when we add an object to a mocked list, the object isn't actually added as the actual add method is not called. However, when we call add on a spied list, the object is actually added as the actual method is called.
Mocking Behavior
Let’s look at how we can use Mockito methods to mock behavior of the mock or spy objects we create.
when() is used to configure simple return behavior for a mock or spy object.
doReturn() is used when we want to return a specific value when calling a method on a mock object. The mocked method is called in case of both mock and spy objects. doReturn() can also be used with methods that don’t return any value.
thenReturn is used when we want to return a specific value when calling a method on a mock object. The mocked method is called in case of mock objects, and real method in case of spy objects. thenReturn() always expects a return type.
To see a list of other available methods, you can refer to this link.
Now, let’s see how these methods can be used for unit testing:
@Test
public void testThenReturn(){
//Create a mock object of the class Calculator
Calculator mockCalculator = Mockito.mock(Calculator.class);
//Return the value of 30 when the add method is called with the arguments 10 and 20
Mockito.when(mockCalculator.add(10, 20)).thenReturn(30);
//Asserts that the return value of add method with arguments 10 and 20 is 30
assertEquals(mockCalculator.add(10, 20), 30);
}
@Test
public void testDoReturn(){
//Create a spy object of the class Calculator
Calculator mockCalculator = Mockito.spy(new Calculator());
//Return the value of 30 when the add method is called on the spied object with the arguments 10 and 20
Mockito.doReturn(30).when(mockCalculator).add(10, 20);
//Asserts that the return value of add method with arguments 10 and 20 is 30
assertEquals(mockCalculator.add(10, 20), 30);
}
Now, let’s look at the common assertions we can use to assert that some condition is true:
assertXYZ()
A set of assertion methods.
assertEquals(Object expectedValue, Object actualValue)
Asserts that two objects are equal.
assertTrue(boolean condition)
Asserts that a condition is true.
assertFalse(boolean condition)
Asserts that a condition is false.
To see a list of all available assertions, you can refer to this link.
With the combination of the above methods and assertions, we can write test cases using the Mockito mocking framework.
Advantages & Disadvantages
Let’s have a look at the advantages and disadvantages of Mockito:
Advantages
- We can Mock any class or interface as per our need.
- It supports Test Spies, not just Mocks, thus it can be used for partial mocking as well.
Disadvantages
- Mockito cannot test static classes. So, if you’re using Mockito, it’s recommended to change static classes to Singletons.
- Mockito cannot be used to test a private method.
There are a few alternative mocking libraries like PowerMock, Robolectric, and EasyMock available as well which can be used.
This was all about how to write basic test cases using Mockito. I hope this blog encourages you to write unit test cases using Mockito or any similar framework. It might seem difficult at first, but once you get the hang of it, it will be of great use.
Published at DZone with permission of Lipika Gupta. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments