Using Appium to Test Fingerprint Authentication on Android Devices
In this article, I'll show how you can use Appium to automate fingerprint authentication on Android mobile devices. The general process also applies to iOS, though specific implementation is not discussed here.
This is based on work I did in preparation for presenting at Mobile Tea Boston in June 2017. This example is just a small part of a broader conversation on automating quality across the delivery pipeline.
Git example: https://github.com/paulsbruce/FingerprintDemo
Fingerprint Security: Great for UX
First question I asked was "why would we integrate fingerprint login functionality into our apps?" The short answer is "high security, low friction". There are compelling use cases for fingerprint authentication.
Paswordless systems usually require people to use SMS or email to confirm login...high friction IMO to the user experience, but who wants their user to leave their UX purposely? This is better security at the cost of poor workflow.
Multi-factor authentication is another good user case. Using biometric ensures that the unique identity of the individual is presented along with additional credentials.
Step-up authentication is another popular method of keeping the run-rate user experience frictionless, yet increasing protection over sensitive information and operations on a user's account.
Fingerprint Security: Bad for Development Velocity
So for teams who want to implement fingerprint authentication into their mobile apps, this also means they need to automate tests that integrate fingerprint security. What does the test automation process look like?
In short, it's a mess. Android libraries and the default UI test framework Espresso contain zero support for fingerprint automation. Since October 2015 with the release of Android 6.0 M, Google provides a standard API for integrating these features into mobile app code, but no way of automating it.
The same is true for Touch ID on iOS, though there are interactive ways to simulate fingerprint events when running XCTest suites in XCode, there is no easy way of writing an automated test that can provide coverage over these workflows.
Without some other automation alternative, these portions of functionality fall prey to the ice-cream cone anti-pattern. What a pity.
Solution: Find the Right Framework
Espresso is fast because it runs directly alongside the main app code on the device. However, since the only way Google provided us to simulate fingerprint events is through ADB (i.e. 'adb -e emu finger touch ...'), this has to be run on the machine where Android tools are installed and where the device is connected.
Appium, an open source outgrowth of Selenium for mobile apps, is architected differently from Espresso and XCTest. Though often slower for this reason, it has some advantages too:
Instead of running directly on the device as a sibling process, Appium tests are executed from a server to which the devices are connected. This provides a context whereby we can inject device-specific commands against the device, in combination with the calls through the testing framework itself, to simulate the entire workflow on the device in one script.
An example of this can be found in my Github FingerprintDemo repo.
Because I want to write all my code and tests in the same IDE, I keep unit tests and Espresso tests as part of the normal conventions in the 'app' module, but I create a separate module called 'appium' that can be compiled as a separate jar artifact from the main APK. This keeps my Gradle dependencies for testing separate from my app and my build.gradle scripts clean and clear.
In short, it boils down to test code that looks like this:
Appium + fingerprint = depends on your lab
If you manage a very small local lab, you have the liability control to execute whatever custom commands you need to on your devices.
If you've graduated to using devices (emulators/simulators/real) in the cloud via some service like Firebase, Perfecto, or TestObject, then your ability to simulate fingerprint events reliably really depends on which one you're using.
For instance, both Perfecto and TestObject provide SSH direct connections to devices, so in theory you could run custom ADB commands against them; Firebase and AWS Device Farm aren't even close to having this capability.
In practice, these cloud services also provide automation endpoints and SDKs to execute these tasks reliably. Perfecto, for instance, has both DevTunnel direct access and scripted fingerprint simulation support in Appium.
Treat Code and Tests as Equal Citizens
Everyone should have access to app code AND test code. Period. Some large organizations often fear that this will leak proprietary secrets to offshore and out-of-cycle testing teams. That's what contracts and proper repository permissions are for.
The benefit for modern teams is that test engineers have better visibility into the app, making test creation faster and initial root cause analysis of defects found faster. In my example, this is what the simplified IDE experience looks like:
Now that we can press the play button on A) our app, B) our unit and Espresso tests, and C) our E2E fingerprint Appium tests, everyone on the team has the option to make sure their changes don't introduce negative impacts on all aspects of the user experience.
'Works on My Machine' Isn't Good Enough
Test code applies first and foremost to the development experience, but also to the build system later on. In the case in including Appium tests in an Android project, this means we need to be keenly aware of the test infrastructure used to simulate fingerprint actions locally against emulators.
Expect that you will need to “productionize” this process to fit into the build process. By introducing a number of new moving parts (emulators, Appium, custom adb commands) we’ll also need to perpetuate that as a build stack.
I’m a Jenkins nerd, so what this means in terms of build infrastructure is that we need to create build nodes that contain the components necessary to run Appium tests in isolation of other processes as well. Emulators keep the solution device-independent and can simplify the test execution logistics, but only provide a very narrow slice of reality.
To integrate real devices into this mix, you either have to manage a local Appium grid (which again, is challenging) or write your tests to use a cloud lab solution. In the end, you'll have to parameterize your tests along the following environment variables:
- Appium server address
- localhost for development workstations and Appium emulator stack in CI
- Shared/cloud host for real devices
- (if emulators)
- emulator image (i.e. Nexus_6_API_24, etc.)
- Device capabilities
- Platform (Android/iOS)
- Platform version
- App (binaries) under test
- (if shared/cloud) credentials or API keys
Recap:
Since there's no support for fingerprint simulation directly from Espresso, we have to rely on other test frameworks like Appium to cover these use cases. Really, the test architecture needs to fit the use case, and Appium provides us a way to mix test framework calls with native commands to other mobile tools. This requires us to introduce complexity carefully, plan for how that impacts our build-verification testing stack when triggered by continuous integration.
More reading:
- Automating the Quality of Your Digital Front Door - Paul Bruce, June 2017
- Why fingerprint security? High-security, low friction.
- paulsbruce@Github: Fingerprint Demo (Android app)
- DevOps.com: when testing mobile apps, use a mobile device cloud
- How to Automate Fingerprint authentication with Perfecto cloud devices