Ever want to do Android UI testing fast, more efficient, and reliable? Then remember the following strategies so you can say the word “Success” when it comes to your debugging task.
Before we proceed, always keep in mind that unit testing is essential to whatever can be unit tested. There should be no shortcut. This is to ensure that your application will run smoothly and free from software bugs.
Android UI Testing
Robolectric and Gradle unit tests support are excellent samples of unit test frameworks for Android. UI tests, on the opposite hand, are wont to verify that your application returns the correct UI output in response to a sequence of user actions on a tool. Espresso may be an excellent framework for running UI actions and verifications within the same process.
The Google+ team has performed many iterations of UI testing. Below we discuss the teachings learned during each strategy of UI testing. Stay tuned for more posts with more details and code samples.
Strategy 1: Using an End-To-End Test as a UI Test
Let’s start with some definitions. A UI test ensures that your application returns the correct UI output in response to a sequence of user actions on a tool. An end-to-end (E2E) test brings up the complete system of your app, including all backend servers and client apps. E2E tests will guarantee that data is shipped to the client app, which the whole system functions correctly.
Usually, to form the appliance UI functional, you would like data from backend servers, so UI tests got to simulate the info but not necessarily the backend servers. In many cases, UI tests are confused with E2E tests because E2E is exceptionally almost like manual test scenarios. However, debugging and stabilizing E2E tests is complicated thanks to many variables like network flakiness, authentication against real servers, size of your system, etc.
When you use UI tests as E2E tests, you face the subsequent problems:
- Extensive and slow tests.
- High flakiness rate thanks to timeouts and memory issues.
- Hard to debug/investigate failures.
- Authentication issues (ex: authentication from automated tests are extremely tricky).
Strategy 2: Hermetic UI Testing using Fake Servers
In this strategy, you avoid network calls and external dependencies, but you would like to supply your application with data that drives the UI. Update your application to speak to an area server instead of an external one, and make a fake local server that gives data to your application. You then need a mechanism to get the info your application requires. This will be done using various approaches counting on your system design. One method is to record server responses and replay them in your fake server.
Once you’ve got hermetic UI tests to lecture an area fake server, you ought to even have hermetic server tests. In this manner, you split your E2E test into a server-side test, a client-side test, and an integration test to verify that the server and client are in sync.
While this approach drastically reduces the test size and flakiness rate, you continue to got to maintain a separate fake server also as your test. Debugging remains tough as you’ve got two moving parts: the test and, therefore, the local server. While test stability is going to be largely improved by this approach, the local server will cause some flakes.
Strategy 3: Dependency Injection Design for Apps.
To remove the extra dependency of a fake server running on Android, you ought to use dependency injection in your application for swapping real module implementations with fake ones. One example is Dagger; otherwise, you can create your dependency injection mechanism if needed.
This will improve the testability of your app for both unit testing and UI testing, providing your tests with the power to mock dependencies. In instrumentation testing, the test apk and, therefore, the app under test are loaded within the same process. Hence the test code has runtime access to the app code. Not only that, but you’ll also use classpath override (the indisputable fact that test classpath takes priority over app under test) to override a particular class and inject test fakes there. For instance, to form your test hermetic, your app should support the injection of the networking implementation. During testing, the test injects a fake networking implementation to your app, and this phony implementation will provide seeded data rather than communicating with backend servers.
Strategy 4: Building Apps into Smaller Libraries
If you would like to scale your app into many modules and views and decide to add more features while maintaining stable and fast builds/tests, then you ought to build your app into small components/libraries. Each library should have its UI resources and user dependency management. This strategy not only enables mocking dependencies of your libraries for hermetic testing but also is an experimentation platform for various components of your application.
Once you’ve got small components with dependency injection support, you’ll build a test app for every element.
The test apps mention the particular Android UI of your libraries, fake data needed, and mock dependencies. Espresso tests will run against these test apps. This permits the testing of smaller libraries in isolation.