How to be a Mock-Star…😎
In this article we will go through the transformation of an ordinary Android developer into a 😎MockStar
😎. We will see how she was forced to write tests which made her use testing tools like Mockito
to make her life easier and then dive a little into the usage and implementation of MockWebServer
. The demo project for this article is available on github.
One fine day, an android developer was feeling pretty awesome in the morning. After spending many days and nights swimming in the ocean of unlimited classes, events and interfaces, she had finally mastered the Rxify
spell, the art of using Dagger
and implemented MVP on her project. Little did she know, what was the true reason behind recommendation of this architecture and all the cool stuff. It was not just distribution of responsibility but also the “thing” which she had managed to avoid almost all her career.
“Testing” — the unavoidable Beast!
Maximum test-coverage for the logic layer
It was only a week earlier when her Project Manager had asked her for the estimates in the sprint planning meeting. She had confidently estimated all the stories without knowing what was coming her way.
Finally, the time came for “The Team Meeting”. The meeting which changed her whole life! Team Lead told her that her logic
layer had to have as much test coverage as possible! She sat like this for the next 15 minutes :
Mockito
She desperately started looking into various testing frameworks and found out the ultimate “potion” to her problems : Mockito!
😀
“Why Drink it?
Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API. Mockito doesn’t give you hangover because the tests are very readable and they produce clean verification errors.” — Mockito.org
She quickly went through the documentation and started on the journey of writing tests. Everything was going great! She was properly Mocking out the views and creating Stubs. While trying to master the art of “when-then”, she had already started dreaming of the MockStar
title.
Testing the Happy Paths of Presenter with Mockito
We have used the cute pokemons API for demo purpose here : pokeapi.
So, she was trying to test the Presenter
as shown in the gist below :
She wrote the test for the above code using Mockito
. She mocked out her View and PokeDataSource
. Have a look at the test for the above code :
when(pokeDataSource.getPokemonAbilityStringObservable(anyString()))
.thenReturn(Observable.just(""));
After the above when-then
blah blah… we test if setApiText()
is called 1
time and showErrorDialog()
is called 0
times. Kindly note that, what actual data is shown, it’s not tested here as it’s not the responsibility of this test case for Presenter. That responsibility lies with our DataRepository
.
Everything was going good, nice and smooth so far. She similarly went on to write the tests for the other cases and finally finished writing the test cases for other Presenters
in her logic layer. She ran code coverage tasks and BOOM! Not Enough Code-Coverage!
Well! That’s Sad. Now she will need to write more tests. She gets back to work and starts writing tests for some error conditions now.
Testing Error cases with Mockito
Now she wanted to test the following embarrassingly ugly if-else-if
mess:
Big Deal? She confidently started to test the above cases by returning the appropriate HttpException
instance. Let’s see the code she wrote :
when(pokeSource.getPokemonAbilityStringObservable(anyString()))
.thenReturn(
Observable.<Pokemon>error(
new HttpException(
Response.error(HttpURLConnection.HTTP_NOT_FOUND,
ResponseBody.create(
MediaType.parse("God Knows What?"),
"Why am I even alive?")))));
“It was not just enough to be able to write the logic tests and use all those cool tools, we have to have maximum test coverage for the logic layer” — She cried in her head.
After a long session of cursing-the-world due to the above “incident”, she got back to work with a cup of tea and clear mind. Then suddenly! it struck her that her whole life has been a lie. She didn’t really have to “Create” an HttpException
when she had Mockito
. She modified her test as follows :
AWESOME! She was able to test all the error cases just like the above. Have a look at her Presenter Test here. She again got a feeling of excitement that this might be the end of all testing-madness finally. She crossed her fingers and ran the testCodeCoverage
tasks.
AND! BAM! Again “Not Enough Code Coverage! OH GOD WHY
She checked the test report and found out thatPokeDataSource
had 0
lines of coverage. Ohhhhh! She still had to test her DataRepository
.
Testing the DataRepository
Next morning, she started testing her DataRepository which had the following public API getPokemonAbilityStringObservable()
:
She again began Mocking stuff and by now she had started to almost hate the word Mock
itself. She would do anything to not use the when-then-what-who-how
anymore.
Now that we are testing the data source, we need to have some sort of mock data that should be provided here, to be able to test the DataRepository
properly. We could continue using Mockito
and write something like :
when(pokemonService.getPokemon(anyString()))
.thenReturn(getMockPokemonResponse());
Main issue with the @Mock
ed PokemonService
is the creation of Mock Response. The method getMockPokemonResponse()
is the main culprit.
It would have still been manageable if she was testing for only pokemon.getId()
. In order to be able to test the functionality properly, she had to feed it all sorts of not-so-cute values of Pokemon
. Have a look at the complexity of the above response here.
She knew that she had to do something better or easier at least. This brings us to the most interesting chapter of her journey towards becoming a Mock Star 😎.
MockWebServer
It has a web server which runs on localhost and can be used to mock responses while testing our applications.
After reading MockWebServer
’s documentation and watching a few episodes of CasterIO videos on MockWebServer by Chiu-Ki Chan, She finally could see it clearly in her head. Her MockStar
title wasn’t too far-fetched now. Let’s see what got into her and how she took the road less travelled and implemented MockWebServer
to simplify the dull and monotonous task of writing tests.
Integrate MockWebServer in your app
She created a separate java module called mocks
where she would store the responses statically in json
format. She named the files in the format : requestMethod_requestPath.json
with all the /
replaced by _
.
Example : GET pokemon/12
would be converted into : get_pokemon_12.json
LocalResponseDispatcher
She wrote a Dispatcher
to map the requests to their json files as follows and returned a MockResponse
object with response code 200
OK :)
With this and plugging-in the MockWebServer
in her repository by creating a test-dependency on the mocks
module: testCompile project(':mocks')
, she was able to get the tests up and running. With the help of Dagger2
and it’s supercool advantage of being able to inject dependencies specific to our tests, she was able to setup MockWebServer
for her unit-tests in no-time. Let’s see how she converted the earlier tests to use MockWebServer.
She changed the Mockito@Mock PokemonService
to @Inject PokemonService.
Testing the DataRepository using MockWebServer
She wanted to again test the following call inside DataRepository
:
She just invoked pokemonService.getPokemon()
within her unit-tests and MockWebServer
took care of the rest with it’s MockResponse
. Have a look at the BaseLogicTest.java
file here for more details.
No need to create any DemoPokemonResponse
just write the logic for your tests and you are good to go! After seeing the magic of MockWebServer
she went all nuts!!
Testing SocketTimeoutException
Now, was the time to test the dreaded SocketTimeoutException
. I think we are really blessed to have Dagger2
in our lives which makes it so easy to configure tests. Just change your OkHttpClient
configuration to builder.readTimeout(2, SECONDS);
and see the magic of MockResponse
's throttleBody()
function, also explained in this video. You can find this test and others such as testing for correct Headers using RecordedRequest
here. For full code sample and setup, checkout this mockstar
repository on Github.
ANNNNNNND You are done!
Let’s run the testCodeCoverage
now!
She finally finished her stories even before her “extended-deadline” and took that time to read some more articles on Medium *cough* facebook *cough*.
Conclusion
Any ordinary android developer can become a MockStar
by using all the right tools and by experience. There’s not just a single way of improving any code we write and I am not comparing Mockito
with MockWebserver
here, they both are extremely advantageous on their own. I think they can be used in conjunction, using the one where it’s needed. We can use Mockito
wherever we do not require actual data and just want to test the conditional flow. And MockWebServer
would come into picture where we need at least some data to be able to test something. Here, I have just covered how we can slightly “speed-up” the boring-but-yet-so-helpful task of writing unit tests in MVP.
I am just an ordinary developer myself — “a wannabe MockStar”, if you feel that there’s a better way, I would like to hear it. Also, the article is just a made up slightly related to the daily lives of developers like us. Let’s all improve and learn together. Kindly click the tiny heart button if you enjoyed this article.
Thank you for Reading :)