This article is intended to serve as a guide on how to do mock tests in Kotlin. To do that, I will briefly explain what mocking is, why it is such an important tool in test-driven development, and what the different types of mocks are. Finally, I will illustrate how to get started with a simple and comprehensive example in Kotlin.
This article is written with the assumption that you are already familiar with testing frameworks like JUnit and the concepts that make up the TDD methodology. If that is not the case, I urge you to explore my other articles on the topic in our library.
However, if you don't know what mocking is in test-driven development or you have never implemented it on your tests, keep reading.
What Does Mocking Mean in Code Testing?
The term "mocking" in the world of development is very specific to unit testing and refers to reprogramming your objects with expectations of the results they are expected to receive.
In essence, mocking means making objects mimic other objects and setting expectations concerning their behavior. This is the act of replicating the behavior of a service, object, or process to facilitate a testing workflow.
Let's illustrate this with an example. Imagine you are tasked with testing the profile page section on the Facebook app for Android. In this case, your responsibilities are limited to confirming that the visual aspect of the application works as intended and there are no issues on the front-end side.
Why Mocking Is So Important in Test-Driven Development?
The purpose of mocking is to make the process of testing code more reliable by focusing on the code that's being tested instead of on how external dependencies behave.
As you can imagine, there are many things involved in the process of displaying the profile page. There are network calls to the Facebook API, database requests, state checks, code dependencies, and other calculations that work together in orchestration to provide everything necessary to display the data.
Triggering all these processes just to test the visual logic of the app would not only be a hassle and a waste of time but actually compromise the accuracy of your tests since there are so many factors that can affect the result of each individual operation. Network connectivity, database state, time of day, and even power can affect the outcome of some operations.
So, in this case, the most viable and productive course of action would be to mock the objects and services that are involved in the profile page so that they always return the same output and behave in a consistent way.
This way, you can isolate the only important aspect of our testing—the profile logic. This reduces complexity and improves performance, making your life easier.
Mocking in Kotlin With Mockito
Alright, it's time to show you how to properly mock a service in a Kotlin project.
To achieve this—and to make things a little easier—I have already created a simple Android app in Kotlin, which contains all the necessary components to design the test. This sample illustrates how a typical app would request data from a service, process the response, and display a profile page, just like what we were already discussing.
First, check out the sample project from this repository and open it in Android Studio or your IDE of choice. Feel free to take your time and explore the project.
Then, create a new Kotlin class in the unit test directory and name it MockingUnitTest.kt.
Once you have done that, add the following code:
As you can see, the mocking process is pretty straightforward.
The first step is to create a mock of the service interface. The reason why we don't mock the actual service class is that we cannot mock classes in Kotlin since all classes are final. You could get around this by explicitly making them not so, but that can be very disruptive and get out of hand in large projects with many classes and services to mock.
Define Your Behaviors
After that, we define the behavior that the mocked object is going to perform. In this case, we are specifying that whenever the isLoggedIn() method is called, the object will return true. Additionally, the second directive states that whenever the getProfile() method is called, the provided profile instance will be returned.
This simplifies a lot of the complexity of the test by isolating the profile logic part of our code. This removes the need to have to retrieve any data from any service. Now you only need to worry about the view part of your code since the rest of the code will behave in a consistent, expected manner.
Then, we need to trigger the behavior to be tested. In this case, we need to invoke the retrieveProfile() method and trigger the mock predefined behavior.
Check Your Assumptions
Finally, we need to set some assumptions and verify if the mock object behaved as expected. Here, the verify() method checks if the mock triggered and whether the resulting behavior is consistent with the predefined behavior.
And there you go.
From here you can then build more complex and in-depth tests to validate the behavior of the app. You can focus on the specific areas that you want to target without worrying about services, networks, or databases.
One simple example would be the following:
Here we are following the same workflow to create the mock and set the expected behavior. However, we can now assert if the profile data matches the expected data. You could easily tailor this to verify if a view is loaded properly by passing data and asserting its properties.
Go ahead and run the tests.
You can find the complete code in my repository.
Other Alternatives
It is important to mention that Mockito is not the only mocking library available for Kotlin. In fact, I would argue that it's not even the preferred one. That would be a library called MockK.
If you want to learn how to work with MockK, you can do so by reading this post.
Mocking Best Practices
It is easy to assume that mocking is a tool that can do no wrong. But that's actually not the case.
There are best practices when it comes to mocking tests, and here are some of them:
- Don't overuse mock. It's important to know when not to use a tool just as much as it is to know when to use it. Some parts of your code do not need to be mocked. This is because either they don't concern the test workflow at hand, or they would be too complex and time-consuming.
- When mocking, only mock objects that contain logic. If the object you are debating whether to mock doesn't contain any logic, then just stub it by passing default values.
- Keep your mocks simple. Don't overcomplicate things by adding scenarios that don't concern the test workflow.
Wrapping Up
Building tests can be a complex matter for individual developers and even experienced teams. Not only are you required to design and develop multiple workflows for use cases, but you also have to dedicate resources to maintain them in parallel with your project. As your project changes, so should your test workflow. And it's understandable how some teams have decided not to follow that path.
Thankfully, there are ways to get around the long investment of resources and still get your app properly tested. if you want to skip the testing process completely, you can check out Waldo's extensive toolset for UI testing. It requires no coding and is very approachable, even for non-developers.
Automated E2E tests for your mobile app
Get true E2E testing in minutes, not months.