Developing the android-base project, I’ve learnt how to cook delicious unit tests, specially designed for the Android view layer. Today I’m going to explain how I prepare them.
With this recipe, you’ll be able to test fragments and activities behavior, making them play like puppets. It can seem complicated to cook and the tests will continue being Android dependent, but the results are very grateful… So I strongly recommend you to try it at home!
1. Make an app that’s easy to test
The basic ingredient of this recipe is an app to be tested, of course. But a simple app doesn’t serve, it must be an app made to be tested. It needs to be implementated following the MVP pattern mixed with the dependency inversion principle (I don’t mean it’s the only way to make an app easy to test, I just list the patterns that we’ll need here).
The views must be passive and free of any logic, moved like puppets by the corresponding presenter. This one is in charge of receiving the view events and send orders to it. The dependency injection with Dagger makes the component replacement easier. Thus, on testing we will be able to mock the presenter and inject it to the view, send our own orders to it and check the callbacks.
We will then be able to test a more concrete chain of events. Furthermore, we isolate the rest of the app, focusing just on possible view related errors.
Note: Actually, ‘views are completely fool’ is a little white lie. Due to Android devices fragmentation, the views are structured in fragments, whom are included in activities depending on the size of the screen. These activities change, for example, if the app is for mobile or tablet, and its navigation too. For this reason, navigation must be placed inside the activities (but just navigation, the rest of the logic must be outside the views).
2. Gradle the ingredients together
Add the android-apt library,
necessary to compile Dagger files, to the project root
Then add all the necessary libraries to your project specific
and define the
Remember to apply the
android-apt plugin and add the
androidTestApt instead of
- JUnit is the framework to structure the tests.
- Mockito will let us mock objects to define their behavior and know when they’re called.
- DexMaker is needed to run Mockito on Instrumentation tests.
- AndroidSupportAnnotations is defined here just to avoid a version dependency problem (because it’s included in other libraries).
- Espresso is the framework to write Android UI tests (espresso-intents are added to test navigation that, as I’ve said, is responsability of the activities).
You need to understand the role of each library to follow this tutorial.
Note: Mockito cannot be updated to version 2
because of some
3. Prepare your first test (not working yet)
You can do it to your taste. I usually launch the activity and get the target test fragment at the setup. Then, I get its presenter and replace it by a mocked one. Here is a test example for a two text field model edition view:
This approach seems to work (and it actually does it most of the times) but it has 2 critical and unacceptable errors:
- You cannot know what has happened with the presenter before the mock replacement.
- Real presenter is initialized and, consequently, all the other components (data repositories, interactors…) when this screen is opened… and that’s not unit testing anymore.
So don’t taste it yet, let’s keep cooking!
4. Make the DaggerTestComponent
We need to take action before the presenter injection is done.
That’s the reason why we have to replace the Dagger
Component and, to do it,
first we must create a test mocker
The plan is to send that test
Component to inject mocked presenters
to the fragments without the knowledge of them.
Create an interface and move there
all the methods from the old dagger
Then, inherit the new interface from the old real dagger component:
And from the new mocker component too:
Finally, implement the module for the test component to return a mocked presenter when required.
Now, we must use the
FragmentInjector type in the fragments, being it
ActivityComponent or a
TestMockerComponent depending on the case
(make the necessary code adjustments to achieve that).
5. Add a test Application
To deal the mocker injector component to the fragment
(who executes the self-injection)
we must move the responsability of the component creation
Application class and replace it by another one
that will return the mocker component.
In my project I have a Dagger Component for the activity scope, and I used
to generate it in the
Application for the
With this structure we must move the creation of the
Application class too, but we can keep the scope by saving the instance on
the activity as a singleton
Application just create a new one on each call).
ActivityComponent is initialized before
to avoid fragment’s
onCreate being called first
like in this case
), that would cause an attempt to fragment injection with a null injector.
Once that is done, we must structure the
Application class in such way that
we’ll be able to override just one method to replace the mocker component.
Note: In this step we can appreciate the scope of each Dagger component,
You must try to adapt it to your own project needs, always keeping
the instances saved in their corresponding place to avoid modifying their scope.
6. Insert the secret ingredient
To replace the
BaseApplication class for the test one we need to create
a test runner, where we’ll be able to select the
Application class to be used.
AndroidJUnitRunner, override the method
and place there the name of your
Change the instrumentation runner in your project
replacing the package name:
At last, the mocked presenter is injected automatically to the fragment, so you can remove its replacement and verify their initialization calls. You can now fix the previous test example:
Also notice that now the test class is marked as
That’s because the rest of the app classes
(that includes database, network, threating… classes)
won’t be used by the mocked presenter
because it works as a firewall.
And that’s it! Enjoy your meal!
With these method implemented, view testing becomes easy peasy. Be creative with the possibilities of this kind of testing, and enjoy it!
Also, you can modify a little bit this recipe and create integration Instrumented tests with a fake api (using MockWebServer or creating a custom one) or other kind of custom tests…
I hope this can be of any help. Don’t hesitate to ask or make suggestions!