Intent testing with Espresso
Most of the time, talking about intents in Android is talking about launching Activities or even deeplinks. And one thing that was missing since now, was the way to test that a certain interaction with your app opens an activity, no matter the kind. To illustrate the problem, I’m going to present you a real case we had here in the Android team regarding this kind of tests.
I don’t know if you ever checked our app, but if you did you may have notice that in our home page there are 4 buttons: Flights, Cars, Hotels and Flight + Hotel.
All of these buttons open new activities, in the case of flights button we are opening our native page, but for the other 3 we are opening new activities with web views.
This made our tests on the home page very slow and heavy, despite the checks we made were very simple. Even most of the time the cars button test was failing with an idle resources timeout exception.
So we had the following problems regarding this test:
- Crushing randomly, so the test was not deterministic.
- We were using an emulator and was crashing very often due to memory issues, we thought, related to this kind of tests.
- We were testing the opening of activities, by actually opening them and checking if one particular element was present in the new activity (textview, image, you can imagine what happens with webviews).
- This tests were attached to the loading of webviews, so depending on the time of load or even the time of animations. This made espresso idle resources go crazy.
- As a result of this, activities were leaking everywhere and so the integrity of the emulator.
- Extra: testing deeplinks was very painful because we needed to mock a lot of things in order to get the activities up and running properly
All the above was breaking the principle of “black box tests” because we were making our tests depending on the result of the next activity loading, which has nothing to do with the actual behaviour of the buttons.
After all these issues we looked for a solution in order to test the opening, closing, parameter passing, and almost everything regarding Intents.
Espresso Intents
We found an extension of Espresso called Intents, which was actually what we were looking for. So in order to be able to use it, first we needed to add it as a dependency as usual.
The second part for this is a little bit trickier. In order to tell Espresso that we want to check intents at some point on our tests we need to call
If we leave the test like that, and in the next test we call again init(), Espresso will throw an exception like this, saying that Intents is still running and there’s no need to call init() again
So in order to avoid this exception we need to call
Here is an example with the setUp() and tearDown() methods initializing and releasing intents.
Also here is the solution we made for our tests at eDreams ODIGEO (irl) it’s a pretty simple ‘traffic light’ system. We wanted to attach the espresso intents anytime we want, with the security that they will detach once the test is done.
But this is not the only option we have to attach and detach Espresso Intents. The Android team offers a new kind of test rule called IntentsTestRule. This rule calls init() and release() before and after setUp() and tearDown as any other test rule would do. The difference with the previous option is that with this rule espresso intents will be attached to every test in the class.
Test that an activity is opening
How can we test that an activity is currently opening? Very easy:
With the intended method, we are checking certain intent is flowing through the app and it matches with the constraints we have put there. In the case above we are checking that there is an intent in the system that has a component inside with the name of a certain class.
In this case we are checking if:
- the intent has a component with the activity name we want to check
- the intent has a key with a specific name
- the intent has an extra with a certain key and certain value
Avoid opening new activities
Ok, now we know how to check that an activity is opened without doing strange checks about its layout hierarchy. Espresso intents offers us the option to respond to certain intents with the method respondWith and Intending
As you can see this action is pretty straightforward. We are telling the system that if there is any intent with a component that has the name of the activity we are after, try to respond to it with a code 0 and null response.
Now the activity is not opening, so we are saving precious seconds and resources for the rest of our test suite.
Test onActivityResult
As you may notice, since we can respond to an intent, then testing the onActivityResult behaviour it’s pretty much the same as before, but with a more elaborate response
Deeplinking test
This is one particular case we had here. We have some cards that once you click on the CTA (call to action) we needed to launch a search. This was done via deeplinks. Again deeplinking is using intents to send information to the system, so once again we can use espresso intents like we have done before to test the behaviour of our deeplinks.
Conclusions
So do what do you think, do we manage to fix all the issues?
- Test crashing randomly, so the test was not deterministic. Solved: we manage to test cars button without crashing anytime
- We were using an emulator and it was crashing very often due to memory issues, we thought, related to this kind of tests. Solved: we managed to solve the issue in this context, emulator is still crashing sometimes for other reasons we still have to figure out.
- We were testing the opening of activities, by actually opening them and checking if one particular element was present in the new activity (textview, image, you can imagine what happens with webviews). Solved: now activities don’t open.
- As a result of this, activities were leaking everywhere and so did the integrity of the emulator. Solved: as said before activities now doesn’t open so they are not leaking.
- Extra: testing deeplinks was very painful because we needed to mock a lot of things in order to get the activities up and running properly. Solved: we finally managed to test deeplinks properly.