It’s generally understood that Calabash can only operate controls that are part of the application it’s testing, but this limitation is particularly annoying with status bar notifications.
We would like to write something like this at the feature level:
We click ‘home’ here with
adb shell input keyevent KEYCODE_HOME because Badoo has special handling when notifications arrive while the app is open; your needs may differ.
The step definitions are trivial:
but the implementation is the key
An indirect approach
Even if you could look at and click on the notifications, there is an argument that you should only test things which are under your direct control. If, for instance, the user interface of Android’s notification mechanism changes radically (e.g. phones vs tablets) it will at the least break your tests, and at worst make them impossible to mend.
One indirect route to checking notifications involves adding a special test mode to the application code that generates notifications, to keep a list of notifications instead of posting them and allowing Calabash to retrieve that list. Calabash can then make assertions about the notification data and issue the intents, to ensure that the application responds correctly. Intents don’t care whether they’re launched by a notification being pressed, so you could test them at multiple points in your app without having to re-generate the notification, if it makes sense to do so.
You might object that this isn’t doing a full round-trip through the notification system so something could go wrong, and you’d be strictly correct, but how often do you change the code that sits between generating a notification and posting it? Do you need to test that every time, if the text and intent given to it are checked? Consider this a form of dependency injection - ‘injecting’ a testable notification mechanism.
A direct approach
There is a more direct route to checking notifications that you may find useful, and it’s the one we’re using for now because it suits our immediate needs.
Everyone knows that Calabash isn’t limited to talking to the phone through its server: most solutions to testing notifications suggest examining the output of
adb shell dumpsys notification to see whether the notification is being displayed, but as mentioned above it’s just as important to test that the pressing of a notification ends up in the correct part of the application.
Calabash can invoke
adb shell uiautomator dump on Jellybean (4.1) and later devices to retrieve an XML document describing the current display. This isn’t particularly fast as an operation, but most applications only have a few notifications to test, so it’s a reasonable trade-off. It’s possible to combine that view list with
adb input swipe to open the notification drawer (on a phone; tablets are more varied) or dismiss a notification, and
adb input tap to press the notification (or the action buttons on the notification, if you’re feeling fancy). Again, not the fastest or most elegant solution, but it’s workable.
A particularly nice aspect of uiautomator’s XML is how easy it makes finding a notification with two strings - you only need a simple xpath:
What follows is our initial code, and should serve as a reasonable starting point.
Retrieving the UIAutomator dump requires a couple of calls: one to create the dump, and one to get it off the phone. I’m using
adb shell cat instead of
adb pull because it saves managing temporary files on the host machine.
We wish to identify a notification by its text strings - it might have one or two. This function builds an xpath for a node whose grandchildren have the given texts - works for us for now, but obviously notification layouts aren’t guaranteed to follow this pattern in future releases.
The UIAutomator dump is mostly
This allows us to run a block of code with the bounds of the first node selected by an xpath - note that the output of uiautomator can involve overlapping views, and it’s not always easy to determine whether a view is effectively clickable. Our more complicated version of this routine clips children to their parents’ rectangles (to account for scrollviews) and checks for potentially overlapping siblings of each ancestor up to the root (we ignore siblings larger than half the size of the parent).
Interacting with the phone
We interact with the phone through
adb input because it’s easy and portable.
This function opens the notification shutter on most phones by swiping down from the top (needs improving for tablets):
This taps a notification (and ensures that the notification shutter is closed)
This swipes a notification to dismiss it and then ensures the notification shutter is closed
Implementing the step
This combines the above code to tap or dismiss a notification, with retry logic if it hasn’t yet been received. The code could be simpler (particularly parameter handling), but we have a Lollipop-specific code path not shown in this article that uses an instrumentation to invoke
UiDevice.openNotification() and related Android calls for more reliable testing on tablets.