Android Test Doubles In Unit Test
Hello everyone! In this article, I would like to mention Android Test Doubles with unit test examples in Trendyol. Anyone who has already coded Unit Test before used test doubles. But what are they? Why do all developers say “mock” or “mocking” for all test doubles? These questions are my main reason to write this article. Let’s dive into the details.
Test doubles are substitute dependency of class which needs to be tested. In other words, we can put test doubles instead of our real dependencies in related classes. Test doubles give us different ways and alternatives how to make our test faster, more reliable and easy to update.
Test doubles are invented by Gerard Meszaros in xUnit Test Patterns. In this book, there are many techniques to explain test patterns so he named all of them differently. These techniques are Test Doubles. There are five different types of test doubles. Let’s have a look at them all separately.
Note: Before the explain test doubles, I would like to mention which mocking library is used in Trendyol. We are using MockK. It is open source mocking library for Kotlin.
Dummy
Dummy objects are just for filling in related required parameters. Any parameters of dummy objects are not used for assertions. When you have big and complicated objects to configure, you can write it dummy version. You do not need any mocking library to create them but it is possible to create them with mocking libraries as well. Let’s give an example of a dummy from Trendyol.
In this example, we try to test whether when the screen status loading, delete buttons are clickable or not. As you see, we need to init our view state. And, it has a cart item parameter. It is not relevant to our test purpose but we need to pass it into an object because it is required and not nullable. So, we create the provideDummyCreditCardItem method which returns the required object as a dummy to just fill the required parameter. That’s the dummy object and its usage in Trendyol.
Fake
Fakes implement the same interface with the class which replaces itself. Therefore, it can act like a real version of itself. It can not be used in production. Its parameters can be used in any assertion. Fakes classes have more simple implementation than real ones. For example, you can write your fake version of the service class. It has a simple configuration.
If you use the Trendyol app and want to add any product to your favourites, you need to log in first. In this test case, we are testing this functionality. To have a simple configuration, we created a fake authenticationFlowUseCase. We hold the isLoggedIn flag in here as you see. We can change the user login status with this flag. Before calling the addFavorite method from addFavoriteUseCase, we are changing the isLoggedIn flag to true from fakeAuthenticationFlowUseCase.
In a test case, we change the user login status as we mentioned before. In a real implementation, if the user is logged in, the repository’s addFavourite method is called. So we are observing this behaviour here. Please be careful that fake class parameter impact test results directly. It is very simple, fast and useful. These are fake class benefits.
Stub
Stubs respond to methods that are expected to be called. They are usually used with mocking libraries. Moreover, it is possible to create manually. If you create manually, you need to create classes and implement the same interface with the real class. And after that, you can call the methods which you expected. Furthermore, it is possible to add new methods which record any information in itself. But if you do it like that, your stubs can act as another test double spy. In Trendyol, we use stub with the mocking library. Let’s look at an example.
This is the view model test case. In this scenario, we are trying to test that our screen needs to be error status when we called fetchPromotionDetail with an error status.
To create this case, we are mocking pageUseCase and passing it as a parameter into the view model. As we mention before, we need to return an error. We use every block while calling our mock object and return an error. It is stubbing. After that, we called our view model methods.
We are stubbing the fetchPromotionDetail method from the pageUseCase because this method is called inside viewModel’s fetchPromotionDetail method and is expected to be called.
Mock
Mocks verify the expected behaviour of the testing scenario. It is implemented with mocking libraries. If you want to make sure that any function was called inside any testing function, you need to use mock. You can observe and verify interactions. By the way, mocks are created from a Class of a type.
In this example, we want to test logic inside the unit. When the user removes his favourite restaurant, the related use case method is called. Inside this call, we need to make sure that the MealRestaurantDetailRemoveFavoriteClick event is called or not. So, we used verify here. exactly means how many times this method is called.
By the way, as you see our analytics variable is annotated with @Mockk. This annotation is served by the mocking library and provides mock objects. It is not a real object but acts like that. It never knows what needs to be returned until it is stubbed. In the test case, analytics.report() method returns unit type so we mention that just Run. This infix and object exist in the library and provide Unit types. If we don’t stub analytics.report() method, the test can not find the answer about this method and it will fail with an exception. As we mentioned before, the mock object is not real. We need to enhance it with stubs.
Spy
It knows as partial mock and works like mock. You can track and observe interactions. If you work with spies, you will work with the real object. Therefore, you can call real methods of objects and use them in any assertions. Real methods act as normal behaviour unless it is stubbed. The important difference from the mock is that spy is created from actual instances of the class.
In this test case, we are testing whether when the process camera image method was called, the private function named “setSelectedFile” is called or not. Firstly, we are creating a spy via “spyk” and enabling the recording of the private call. It is special for spy and also the main difference from mock. By the way, we are creating a mock File object here and stubbing for the expected answer. If you have a look verify, we are giving the expected private method with its arguments.
In this article, I mentioned test doubles, their differences and their usage in Trendyol. I hope you like and learnt them all. Thank you for your reading 🙌
References