Mockito is a popular open-source mocking framework for Java and Android development. It allows developers to create and configure mock objects, which are useful for isolating components under test from external dependencies, such as databases, web services, or other components. Mockito simplifies the process of creating mock objects and provides a clean, easy-to-read syntax to define and configure them. In this article, we will discuss the fundamentals of the Mockito framework and demonstrate its usage in Android development.
In software testing, mocking refers to the practice of creating simulated objects that mimic the behavior of real objects in a controlled manner. Mock objects are typically used when testing a specific component or unit of an application, where the goal is to isolate it from the rest of the system. By using mock objects, developers can ensure that the component under test is not affected by any external factors, making it easier to pinpoint the source of any issues that may arise during testing.
To get started with Mockito, you first need to add the proper dependencies to your Android project. In your build.gradle
file, add the following lines to the dependencies
block:
testImplementation 'org.mockito:mockito-core:3.11.2'
androidTestImplementation 'org.mockito:mockito-android:3.11.2'
NOTE: Replace the version numbers with the latest available Mockito versions.
With Mockito, creating a mock object is as simple as calling the mock()
method:
MyClass myMock = mock(MyClass.class);
This creates a mock object of type MyClass
. You can now use this mock object in your tests, just like you would use a real instance of MyClass
.
One of the main features of Mockito is the ability to define custom behavior for methods in your mock objects. This is known as stubbing. To stub a method, you use the when()
method, followed by the thenReturn()
method:
when(myMock.myMethod()).thenReturn("Hello, World!");
In this example, we’re stubbing the myMethod()
method in myMock
to return the string "Hello, World!"
when it’s called. You can also use the doReturn()
method for stubbing:
doReturn("Hello, World!").when(myMock).myMethod();
Both approaches achieve the same result, but the doReturn()
method is recommended when dealing with void methods or methods that throw exceptions.
In addition to stubbing methods, Mockito allows you to verify that specific methods were called during the execution of your tests. To do this, you use the verify()
method:
verify(myMock).myMethod();
This line verifies that the myMethod()
method was called exactly once on myMock
. If the method was not called, or if it was called a different number of times, the test will fail.
In Android development, Mockito is particularly useful for testing components such as Presenters, ViewModels, or Repositories, where you may want to isolate them from Android-specific dependencies, such as Context or Services.
To demonstrate Mockito in action, let’s consider a simple example of testing a ViewModel. We’ll use the following ViewModel class:
public class MyViewModel {
private MyRepository repository;
public MyViewModel(MyRepository repository) {
this.repository = repository;
}
public String getMessage() {
return repository.fetchMessage();
}
}
In this example, we have a ViewModel that depends on a MyRepository
object to fetch a message. We want to test the getMessage()
method in isolation, so we’ll create a mock of the MyRepository
class and stub its fetchMessage()
method:
public class MyViewModelTest {
private MyViewModel viewModel;
private MyRepository mockRepository;
@Before
public void setUp() {
mockRepository = mock(MyRepository.class);
viewModel = new MyViewModel(mockRepository);
}
@Test
public void testGetMessage() {
// Stub the fetchMessage() method in the mock repository
when(mockRepository.fetchMessage()).thenReturn("Hello, World!");
// Call the getMessage() method in the ViewModel
String message = viewModel.getMessage();
// Verify that the fetchMessage() method was called on the mock repository
verify(mockRepository).fetchMessage();
// Assert that the getMessage() method returns the expected value
assertEquals("Hello, World!", message);
}
}
In this test, we’re using Mockito to create a mock of the MyRepository
class, stub its fetchMessage()
method, and verify that it’s called when we invoke the getMessage()
method in the ViewModel. Thisway, we can test the ViewModel’s behavior in isolation, without having to worry about the actual implementation of the MyRepository
class.
In addition to the basic features discussed so far, Mockito offers several advanced features that can help you write more powerful and flexible tests.
Sometimes you may need to stub a method that accepts arguments, but you don’t care about the exact values of those arguments. In such cases, you can use argument matchers, which allow you to define a more general condition for the arguments. Mockito provides several built-in argument matchers, such as any()
, eq()
, and isNull()
.
Here’s an example of using the any()
matcher:
when(myMock.myMethod(any(String.class))).thenReturn("Hello, World!");
In this example, we’re stubbing the myMethod()
method to return the string "Hello, World!"
when it’s called with any String
argument.
If you need more control over the behavior of a stubbed method, you can use the thenAnswer()
method to provide a custom implementation. This method takes an instance of the Answer
interface, which defines a single method, answer()
, that will be called when the stubbed method is invoked.
Here’s an example of using the thenAnswer()
method:
when(myMock.myMethod(any(String.class))).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
String arg = (String) invocation.getArguments()[0];
return "Hello, " + arg + "!";
}
});
In this example, we’re stubbing the myMethod()
method to return a custom string based on its input argument. The answer()
method receives an InvocationOnMock
object, which provides access to the arguments passed to the stubbed method.
By default, Mockito cannot mock final classes or methods. However, you can enable this feature by creating a file named org.mockito.plugins.MockMaker
in your project’s test/resources/mockito-extensions
directory, and adding the following line:
mock-maker-inline
With this configuration, Mockito will be able to mock final classes and methods, although doing so may have some limitations and performance implications.
Mockito is a powerful and flexible mocking framework that significantly simplifies the process of creating and configuring mock objects in your Android tests. By using Mockito, you can write more focused, isolated, and reliable tests, which ultimately leads to a more robust and maintainable application.
In this article, we have covered the basics of Mockito, including creating mock objects, stubbing methods, and verifying method calls. We’ve also discussed some advanced features, such as argument matchers, stubbing with callbacks, and mocking final classes and methods. By incorporating Mockito into your Android testing toolkit, you’ll be better equipped to tackle a wide range of testing challenges and ensure the quality of your application.