Espresso Test Recorder

About Espresso

The Espresso testing framework, released by Google in October of 2013, is part of the Android Testing Support Library - a set of APIs that allow you to build and run test code for your android applications. This library provides tools such as JUnit 4, UI Automator, and, of course, Espresso.

The Test Recorder

At Google IO 2016, a new tool for Espresso was demonstrated: a recording tool that allows the user to run the app on the device or emulator of their choice and record user interaction directly, and then translating that interaction into editable and reusable test code.

At the time of this blog (early August 2016), this feature is not yet deployed into the stable, release build of Android Studio; it is instead available starting in Android Studio 2.2 Preview 3, which can be downloaded through the Dev or Canary release channel.

Update

As of September 1st, the Android Studio 2.2 RC build was released to the Canary Channel and is a more stable version that includes the Espresso Test Recorder tool. It will be slowly propagated up to the other channels (Dev, Beta, and eventually Stable). You can read the release announcement here.

Table of Contents

How to Use Espresso Test Recorder

Once you have Android Studio 2.2 Preview 3 (or higher) installed, it's time to start recording your Espresso Test!

Where to Find the Tool

You can find the tool in the top Application menu - go to:

Run -> Record Espresso Test

Go to Run -> Record Espresso Test

A dialog will appear where you will select your target device.

Selecting a Target Device (Emulator vs Physical Device)

According to the documentation, Espresso Test Recorder will support a variety of devices, including physical devices, virtual devices, and even cloud devices! If you need to, go ahead and create a new Emulator, or connect your physical device. Now all you have to do is select it under Connected Devices.

Selecting a Target Device

Recording the Test

Some Background

For my app, I've got 4 simple buttons taking you to different parts of the app. The first, top-left button, called "Colors", takes you to a colors sample page that allows the user to view a set of color palettes, and then copy the hex values to their clipboard.

I'm going to create three tests:

Test 1)

We're going to to tap the "Colors" button (which will take us into the ColorsActivity, and then tap on the first palette (the blue one). According to my app, the user "selects" the color by tapping on the palette (a CardView containing the color and its lighter and darker accents), which will then pop up a Snackbar message, telling the user that they've selected that color. That's it. Two taps. We're going to add the assertions for this test manually.

Test 2)

The Floating Action Bar, or FAB, at the top of the screen is to copy the selected color to the clipboard by showing a NEW Snackbar with a Copy action. Now we're going to repeat the first two steps of the first test, and then we'll tap the FAB, tap the copy action inside of the Snackbar message that pops up. Now we'll verify the text has, indeed, been added to the clipboard by pasting it in a edit text at the bottom of the screen, and then verifying the text is what we expect.

Test 3)

We know that if we tap the FAB after a palette has been selected that a Snackbar will appear with the color we have selected and an action to copy the hex value of the color. But what if we haven't selected a palette yet? This time we're going to Tap the "Colors" button to go to the ColorsActivity, but instead of selecting a color, we're going to tap the FAB immediately to make sure our in app error handling is working.

Time to Record

Now it's time to create a test (Test 1). Keeping it simple - all we're going to do is a two tap recording - then we're going to add a custom assertion inside of the code that generated for us after pressing Complete Recording.

Once you have the Record Your Test dialog pop up, it's time to start pressing buttons on your device (or emulator).

In our case, we'll tap the "Colors" button to go to the ColorsActivity, and then tap the first color-palette CardView - the blue one.

The App Home Screen

Using the tool and recording a test - naming the file

After you press Complete Recording, another dialog titled Pick a test class name for your test will appear. We're going to name this ColorsActivityTest since it will be testing the functionality of the ColorsActivity in my app.

Using the tool and recording a test - naming the file

The Generated Test Code

The Espresso Test Recorder tool has now created and opened our complete test! Wow! You have a whole Espresso test built with a few clicks and taps! But that's not all. You can edit the test however you'd like, just as if you were writing Espresso tests without the recorder.

We'll be adding a custom assertion next.

The generated code

Editing Generated Code

Now that we have our first interactions written for us, we want to make sure the Snackbar that pops up after we tap the blue color palette shows the correct text!

You can easily test this with one assertion. Let's take a look:

The edited code

We've added a new assertion:

onView(allOf(withId(R.id.snackbar_text), withText("Selected Blue with Hex of #ff00a1e0"),
 isDisplayed())).check(matches(isDisplayed()));

Running Your Tests

Once you've finalized your UI tests, you can run them very easily by right-clicking on the test class inside the Project Tool Window, and select:

Run 'ColorsActivityTest'

Running the test

You can also run the test by opening the file and using the hotkey (on a Mac):

[control] + [R]

Now you can simply select the deployment target device to run the test on, and hit 'OK'

Running the test - Select Deployment Target

Once the tests complete, you can see whether or not they've passed. We see that the one simple test we've written has passed - all green! Let's take a look at the other two test and get recording.

Running the test - Results

Two More Tests

Test 2

As mentioned previously, we want to make sure of our ability to add text to the clip board and then paste it into our edit text succeeds.

-Spoiler- -- Espresso cannot actually handle pressing the system "Paste" button, since it's a system menu, akin to a standard Android Context Menu. The recorder will (successfully) add the line to test it, but running the test will FAIL. Not covered in this tutorial is another tool called UI Automator that is better suited for this action. In the mean time, we'll comment out the line in the generated code that attempts to invoke the "Paste" button, and leave the line where the recorder conveniently writes code that replaces the text using "replaceText" in order to have the test pass.

Now back to the tutorial.

Adding Assertions

Before we add assertions, let's record our actions up to the point where we've pasted the text into our EditText:

1) Tap the "Colors" button to get to the ColorsActivity

2) Tap the blue color pallet to select it

3) Tap the FAB to show the Snackbar indication our selected color and showing the action to copy the hex value to out Clipboard

4) Tap the "Copy Hex Value" action inside the Snackbar

5) Tap inside of the EditText to request focus

6) Long press inside of the EditText to show the system "Paste" button

7) Tap the "Paste" button

Now we're going to build our first assertion using the recorder. In the recorder, click the Add Assertion button. This will cause the recorder to gather a snapshot of the screen showing on the device (or emulator) and present that snapshot with selectable views for which to create the assertion. You can also select the view from the dropdown that says "Select and element from screenshot".

Adding an Assertion

After clicking on or selecting the EditText view, the recorder gives a list of assertions to apply to the view. Luckily, the one we want is the default - "text is". The value we want to assert is ALSO the default - the blue hex value we pasted.

Adding an Assertion - Complete

Click Save Assertion and then Complete Recording, and the test name dialog will once more pop up. Unfortunately, there doesn't seem to be a way to add the test to an existing class, and typing in the same name causes an error complaining that the test already exists - so, for now, we'll call it ColorsActivityCopyPastingTest.

Remember to comment out or delete these lines:

 ViewInteraction appCompatButton2 = onView(
 allOf(withText("Paste"), withContentDescription("Paste"), isDisplayed()));
 appCompatButton2.perform(click());

If you wanted to test this with just Espresso, you could create another button to perform a manual paste into the EditText using the ClipboardManager. Then you could remove the generated code (that replaces the contents of the EditText with our expected results) shown below:

 ViewInteraction appCompatEditText2 = onView(
 allOf(withId(R.id.et_color_edit_text),
 withParent(withId(R.id.ll_color_cards)),
 isDisplayed()));
 appCompatEditText2.perform(replaceText("#ff00a1e0"));

With multiple tests, you have options: you can either cut/paste the new test method called colorsActivityCopyPastingTest() into our existing ColorsActivityTest and delete the ColorsActivityCopyPastingTest class, or you can keep it separate. I personally wanted to keep them in one class since they're all testing the ColorsActivity. Now the next time you run the test class, all the @Test methods in the class will run sequentially.

-Note- -- I should mention that the assertion for clicking the action inside of the Snackbar will sometimes not register in the test and the test will fail if you have not turned off animations on your device. Espresso does not play well with animations and Google suggests turning off window animations, transition animations, and animator durations. See more about that here. Normally, if you need a pause (beyond animation), Espresso has methods for handling idling with your own classes. You would not use Tread.sleep(), but instead implement your own classes using Espresso's IdlingResource class to handle things with delays or asynchronous execution. There is a nice tutorial on automatically disabling animations on test devices using a custom test Rule, permission, and manually granting that permission via Gradle. You can find that tutorial here.

Test 3

Test 3 is simple - we want to make sure that if no color palette is selected and the user taps the FAB, the Snackbar shows our error handling message instead of the selected color and an option to copy the hex value to the clipboard.

-Spoiler 2- -- once we've created this method, if we keep it in the same class as the other test, this test should run first, since the selected color state remains from taping the blue palette in the other test, which would fail our conditions for this test if it comes after them. You can override some methods with Espresso to set states how you'd like, but for now, I'm going to be lazy and just put it first.

Let's get recording again:

1) Tap the "Colors" button to get to the ColorsActivity

2) Tap the FAB button

Recording the third test

That's it. Click Complete Recording in the Test Recorder and just like last time, let's call it something unique (I called it "ColorsActivityErrorHandlingTest") and then cut/paste the new @Test method into our original test class and delete the gutted, newly generated class. Remember to put this test method first.

Also we're going to add in the code for checking the snack bar that we did in the first test:

 onView(allOf(withId(R.id.snackbar_text), withText("Please select a color"),
 isDisplayed())).check(matches(isDisplayed()));

All Together Now

Now you can repeat running the test by right clicking on the ColorsActivityTest class and clicking Run 'ColorsActivityTest' in the context menu. This will run all three tests and give you the results.

Running all the test - Results

What This Tool Can Do

The Espresso Test Recorder is meant to save time for the developers writing the automated UI tests, and does a lot for you by taking away a large chunk of repeated UI integration steps, and allows introducing alternate flows to do the same things. The examples I gave were very bare bones and were more focused on introducing the tool itself - but there is plenty more you can do with the tool.

View Matchers

The main time saver of the Test Recorder is its ability to grab the UI layout and parse which view you want to add assertions to. Instead of having to determine what the position of child item is in a list or what ID a view has manually, the recorder takes the dump of the UI layout and determines that for you. Later on, if you're not satisfied with how the view is matched in the generated code, you can modify it to be more to your liking.

Types of Assertions

Depending on the type of component you select, there are a variety of assertions available to you, even though they are currently somewhat limited. Here are a few assertions that I discovered while using the tool:

1) exists - this assertion can be used on just about any view. It's converted into the matches() assertion in the generated code.

2) does not exists - this assertion can be used on just about any view. It's converted into the doesNotExist() assertion in the generated code.

3) text is - this assertion can be used on about any text-based view - such as an EditText or a TextView, though I noticed it was missing for a Button. It's converted into a matches(withText("[SOME_TEXT]")) assertion in the generated code.

Types of Interactions

The recorder is capable, as shown, of recording taps on the screen (but not always long presses, *see below), but I can also record several other interactions. So here are some of the interactions I found are recordable:

1) Tapping on components (such as buttons)

2) Long Pressing components (such as on EditText)

3) Typing into an EditText is easily recorded

4) Taping on a Spinner (Dropdown menu) and selecting an item

What This Tool Can Not Do

Capibilites

Presently, the recorder was unable to change interactions once they occurred. If you clicked on the wrong element, or click too many times on a button, there was no way to remove or edit that event from inside the recorder. You would have to either restart, loosing all your steps, or modify the generated code after completing the recording.

Duplicating steps or moving steps are also capabilities missing from the tool. I would bet these basic interactions will become available in the future, but for now they are missing.

Missing Types of Assertions

I could not find any options for other assertions beyond matches() and doesNotExist() assertions. The withText() method is actually a View Matcher, not an assertion, so the text is option in the recorder is just a matcher/assertion combo.

Other View Assertions, such as selectedDescendantsMatch(), Layout Assertions, such as noEllipseizedText(), or Position Assertions, such as isLeftOf(), do not exist in the tool yet. However, I would assume many will be on the way.

Missing Types of Interactions

The recorder, I noticed, was sometimes unable to determine a long press from a tap. Espresso accepts long presses, so it's easy to change in the generated code. The recorder is capable and sometimes does record the long press - simply be aware while recording your test that the long press may not be recognized.

Swiping through a ViewPager also seems to be ignored in recording. Espresso can actually handle swiping, but the recorder does not pick up on this interaction.

Scrolling is also not currently recordable through the tool - but Espresso itself does allow scrolling through a scrollTo() method.

The tools is also unable to detect system components, such as the copy or paste buttons. If you want to add an assertion to those components, the tool will not be able to find it in the view hierarchy dump. The gotcha here is that pressing the paste or copy button would actually be recorded, but the generated test would fail when running since Espresso cannot find the view.

Conclusions

The Espresso Test Recorder is a extremely useful tool for writing UI automated testing. Espresso can run on Jenkins and works well with other continuous integration tools, making this a great way to quickly write test coverage for your application's UI. Using this tool also has the expressly important point of approaching your app from a user perspective, writing tests from a flow that the user would take. This alone makes for great test writing.

Other than making test writing faster - it also does so in an easy to read way. The generated code is very easy to understand, even if sometimes the naming conventions are a bit simplistic. Since it's editable, that's easy to fix.

One of main pain points I found using this tool was there was no way to add new tests functions to an existing test class, bringing some unneccessary overhead to the tool. I also found that it didn't work well with standard android UI interactions, such as the Snackbar dialog or the Paste dialog.

Over all - the Espresso Test Recorder is a huge improvement to Android studio, a great feature that I'm glad they showed off at Google IO 2016. Please check out the link to the IO demo below, along with some other great reference material, and, of course, the demo code I used for this tutorial.

Example Code and References

GitHub Example Code

Espresso Test Recorder

Android Studio 2.2 RC

Android Studio 2.2 Preview 3

Video from Google IO 2016 for Espresso Test Recorder:

Espresso Documentation

Android Testing Support Library

Automated Animation Disabling for Espresso

Bonus! Android Studio Testing Codelab

About the Author

Jack HugesJack is an Android Developer in CapTech's System Integration practice and is based out of their Richmond, VA office. He has a passion for enterprise level Android architecture and integrating micro-services with mobile.