Espresso is a testing framework for Android that makes it easy to write reliable user interface tests directly within the Android Studio Integrated Development Environment (IDE). Espresso is targeted at developers who understand that automated testing is an integral part of the development lifecycle. Understanding how to use Espresso is fast becoming a required skill for developers to have, particularly those developers focused on mobile application development.
While it can be used for black-box testing, Espresso’s full power is unlocked when the codebase under test is exposed and accessible at the source code level. Once Espresso can run against source code, a whole world of possibilities opens up in terms of mobile testing. Not only can you can use Espresso to do a mobile website test against browsers that are embedded in cell phone and tablets, but you can use Espresso as a mobile app testing tool to run against features in a native application.
Regardless of the direction you take in mobile testing, browser based or testing against native applications, Espresso is powerful and flexible. Also, because Espresso is built right into the Android Studio Integrated Development Environment (IDE), developers can get up and running in short order.
In this article we’ll take a look at the particulars of using Espresso to do mobile testing under Android Studio. In addition we'll discuss the making applications easy to test by separating UI code from business logic. Lastly, we’ll look at how to create useful Espresso test reports from within Android Studio.
Before delving into the details of automated mobile testing using Espresso, it’s important to understand an essential concept in software testing -- creating testable code.
One of the common mistakes made by developers new to automated testing is failing to create code that is testable. Ensuring that an application is testable is a practice that is best done when the code is being designed, not later in the software development lifecycle.
A simple analogy is being able to test a tire’s air pressure on an automobile. All those involved in making a car understand that checking tire pressure is an essential feature of the vehicle. Thus, those designing the car will do best to make sure that the air valve is on the outside of the tire, easily accessible to the driver for testing. Putting the air valve on the inside of the tire forces the driver to crawl underneath the chassis to find the air valve in order to apply the pressure gauge. The bad design hinders the ability of the driver to check a tire’s pressure easily and quickly. Of course, no automobile designer in his or her right mind would put the air valve on the inside of a tire. But, there are more than a few instances out there where developers made code that’s hard to test or completely untestable. To use our analogy, they put the air valve on the inside of the tire. Usually the developer writing such code was not responsible for testing the code written, or had no knowledge of the testing that needed to be supported. Thus, the code ended up downstream in the software development lifecycle (SDLC), in the hands of a QA engineer given the task to figure the testing out. This is money wasted. The rule of thumb in software development is that the cost of work increases as the code goes further down the SDLC. Determining test requirements and testing implementation at the design time saves money and increases the velocity of release overall.
One of the best ways to ensure that a given codebase is testable, is to make sure that the code is well encapsulated and that it follows the design principle of separation of concerns. At the least, the UI code should be distinctly separate from the code for business logic and data access. Figure 1, below, illustrates the concept.
Figure 1: Separating UI functionality from business logic is a good practice not only for programming, but also for testing
Fortunately, the mechanism for separating UI Code from business logic code built into the Android Studio project framework. Android Studio organizes UI tests under the category name, androidTest and business logic tests under, test. (You’ll see the automatic categorization of tests in the next section, when we review using the Espresso Test Recorder.)
The important thing to remember is that business logic code should be separate from UI code. Separating UI from business logic code is not only a good practice in terms of software design, but makes testing a lot easier.
One of the most useful testing features of Android studio is the Espresso Test Recorder. The Espresso Test Recorder allows developers to create Espresso tests by automatically recording interactions performed upon a device emulator. For example, developers can select UI elements such textboxes and dropdowns as well as option and checkboxes into which data is entered via keyboard screen tap. All the activity is recorded by the Test Recorder. Once recorded, a developer can use the script that is result of the recording as a template upon which to build more comprehensive tests.
Listing 1 below show a snippet of UI test code created using Espresso Test Recorder. This test code facilitates a button click and confirms that the textarea displays text.
@Test
public void simpleButtonClickTest(){
ViewInteraction appCompatButton = onView(
allOf(withId(R.id.button), withText("Get Saying"), isDisplayed()));
appCompatButton.perform(click());
ViewInteraction viewGroup = onView(
allOf(childAtPosition(childAtPosition(withId(android.R.id.content),
0),1), isDisplayed()));
viewGroup.check(matches(isDisplayed()));
}
Listing 1: The Espresso Recorder records interactions with the UI into code in a test file.
You access the Recorder from the Run menu item in the Android Studio menubar as shown below in Figure 2.
Figure 2: The Espresso Test Recorder is built right into Android Studio
The Recorder will fire up a dialog in which you to choose a device emulator to use. For example, you can choose a generic x86 or ARM emulator. Or you can pick an emulator that is more specific, such as a Galaxy or Pixel device. Of course, the developer can always choose to connect a real mobile device to his or her development machine and test against the real hardware. Then, the developer goes about making the gestures and entering data that is relevant to the scope of testing underway. The Recorder allows a developer to add an assertion after each data entry event. Being able to add assertions as part of the recording sessions saves significant time test creation process. Listing 2 below shows a test, simpleResponseTest that is the result of a test recording session in which an assertion is declared while recording.
@Test
public void simpleResponseTest() {
ViewInteraction appCompatButton = onView(
allOf(withId(R.id.button), withText("Get Saying"), isDisplayed()));
appCompatButton.perform(click());
ViewInteraction textView = onView(
allOf(withId(R.id.textView), withText("Be Kind To Strangers"),
childAtPosition( IsInstanceOf.<View>instanceOf(android.view.ViewGroup.class), 1),
0),
isDisplayed()));
textView.check(matches(withText("Be Kind To Strangers")));
}
Listing 2: The Espresso Recorder allows the developer to add assertions during a recording session
Once you finish a recording session, the Recorder will ask you to declare the name of the file in which to save the recorded code. The Espresso Test Recorder has the intelligence to store the test file in a category named, androidTest, as shown below in Figure 3.
Figure 4: Android Studio distinguishes between device UI tests (androidTest) and business logic unit tests (test)
As mentioned earlier, Android Studio segments UI tests from business logic unit tests. UI Tests will be tested under Espresso against the chosen device emulator invoked automatically by Android Studio. Business logic tests will be run using JUnit from within Android Studio too. After the tests are run Android Studio reports the results of the various tests. Test reporting is a critical feature of Android Studio in general, and Espresso in particular.
When it comes to ensuring the quality of a given code base, there are two basic metrics that are typically used to quantitatively determine code quality. These metrics are Pass/Fail percentage and Code Coverage.
The Pass/Fail metric reports the number of tests that have passed. The Code Coverage report indicates the lines of code that have been exercised by a test. The general practice is that you want your tests to exercise all the lines of code that have been written. Code that has not been exercised by a test is always a risk.
The goal in terms of an adequate testing practice is make sure that all the tests that have been created for the application pass and that those tests exercise all the lines of code in the codebase. In other words: all tests pass and code coverage is as close to 100% of the codebase as possible.
You select the test or test package you want to run in and Android Studio project pane. Then right click the selection. You can Run the test, Debug a test, or run the test with Coverage, as shown below in Figure 5.
Figure 5: Running tests in Android Studio
When you select the coverage option, not only will the test be executed, but also a coverage report relevant to the test will be created.
After a test is run, Android Studio shows the results in the Run pane on the lower left of the IDE as shown below in Figure 6. Also, should you have run the tests with coverage, the coverage report will be available in the pane in the upper right of the IDE.
Figure 6: Android Studio displays Pass/Fail and Coverage information within the IDE
In addition to being able to view the Coverage Report from within the IDE, you can export the Coverage Report into a set of HTML documents that allow drill down inspection. You export the coverage report by clicking the Export button on the left side of the Coverage Pane, as shown in Figure 6, above. You will be asked to declare a location to save the HTML file set. Once you save the coverage report you can view it in your browser.
Figure 7 below shows an excerpt from a Coverage Report generated by Android Studio.
Figure 7: Lines of code that have been exercised by UI tests are highlighted in green in the Coverage Report
The Coverage Report displayed in Figure 7 indicates that all the lines of code written in the class, SayingsFactory
, has been exercised by a unit test within Android Studio. 100% test coverage for a class is excellent. However, just because one class has 100% coverage, it does not necessarily follow that the entire codebase is well exercised. The goal is to have a high coverage number, as close to 100% as possible for the entire codebase. Thus, developers and testers need to design a testing implementation that makes sure that a high coverage percentage is achieved for of the entire codebase.
Espresso is a powerful tool that enables Android developers to implement mobile testing easily and comprehensively. Developers can use tools such as the Espresso Test Recorder from within Android Studio IDE to make mobile testing an integrated part of the overall software development experience. Also, the reporting features available within Android Studio allow developers to make sure that the code under test meets essential quality requirements. Integrating test reporting into Android Studio allows developers to find and address issues when writing code, not later on downstream in the software development process when making fixes becomes more expensive.
Comprehensive testing is an essential part the Software Development Lifecycle. When a developer takes the time to master Espresso, he or she will realize returns that far outweigh the investment made. Modern developers understand creating and executing both UI and unit tests continuously throughout the Software Development Lifecycle are critical requirements for making quality code. Espresso is an important tool that will help developers make the tests that count in order to make software that counts.