Barista — Enjoyable Espresso Android UI Tests

Leonel Menaia Dev
ITNEXT
Published in
3 min readMar 11, 2022

--

Barista — The one who serves a great Espresso

Barista makes developing UI tests faster, easier, and more predictable. Built on top of Espresso, it provides a simple and discoverable API, removing most of the boilerplate and verbosity of common Espresso tasks. You and your Android team will write tests with no effort.

Espresso is reasonable for UI tests but it has some problems. The code is verbose and the API offers almost no out-of-the-box functionalities. Most of them need to be custom-built.

How does Barista simplify things?

Firstly, let’s take a look at an Expresso example.

onView(withText("Button")).check(matches(isDisplayed()))
onView(withId(R.id.button)).perform(click())

These examples are very verbose. It is needed a lot of code to do something simple. There are multiple functions and arguments inside arguments. Let’s observe the Barista alternative.

clickOn(R.id.button);
assertDisplayed("Button");

The code is straight to the point and on a larger scale more readable for someone with not a lot of context, even to non-programmers.

These functions can also be used with different arguments, either with a string, a view id, or a string id.

clickOn(R.id.button);
clickOn(R.string.button_text);
clickOn("Next");
assertDisplayed("Hello world");
assertDisplayed(R.string.hello_world);
assertDisplayed(R.id.button);
assertDisplayed(R.id.button, "Hello world")
assertDisplayed(R.id.button, R.string.hello_world)

Complex Example

Let’s take a look at a complete Espresso instrumented test.

Now the Barista alternative:

The code becomes so intuitive that it renders comments obsolete. The code becomes the documentation.

List Tests

Interacting with lists is very easy with Barista. While on Espresso, it is needed a lot of code and there is a distinction between ListView and RecyclerView, on Barista, the methods are interchangeable.

clickListItem(R.id.list, 4);
clickListItemChild(R.id.list, 3, R.id.row_button);
scrollListToPosition(R.id.list, 4);
assertListItemCount(R.id.list, 5)
assertListNotEmpty(R.id.list)
assertDisplayedAtPosition(R.id.list, 0, "text");
assertDisplayedAtPosition(R.id.list, 0, R.id.text_field, "text");
assertDisplayedAtPosition(R.id.list, 0, R.string.hello_world);
assertDisplayedAtPosition(R.id.list, 0, R.id.text_field, R.string.hello_world);
assertCustomAssertionAtPosition(R.id.list, 0, customViewAssertion);
clickSpinnerItem(R.id.spinner, 1);

Interacting with Espresso in a ScrollView requires to scroll to each view, which is not very reliable. To keep tests simpler, Barista scrolls automatically before interacting with any View, and only does it if needed.

Expresso also doesn’t work with NestedScrollView.

When interacting with ViewPager, the same views may be on multiple fragments. Barista only interacts with what is displayed, so AmbiguousViewMatcherException can be avoided.

Dealing with Flaky Tests

Flaky Tests are a nightmare to deal with. From the test environments not being the same to views not appearing at the right time, there are numerous reasons that tests can become flaky. Barista provides some annotations to deal with this.

Tests need to be deterministic, but when everything fails Barista helps with that. The @AllowsFlaky annotation allows to define the number of times that the tests are run, and if they pass one time, it will count as a successful test.

The @Repeat annotation can be used so that the test only pass if all execution pass. This can be useful for extra validation of a test or even to locally test a flaky test that fails on the Continuous Integration.

// Use a RuleChain to wrap ActivityTestRule with a FlakyTestRule
private ActivityTestRule<FlakyActivity> activityRule = new ActivityTestRule<>(FlakyActivity.class);
private FlakyTestRule flakyRule = new FlakyTestRule();

@Rule
public RuleChain chain = RuleChain.outerRule(flakyRule)
.around(activityRule);


// Use @AllowFlaky to let flaky tests pass if they pass any time.
@Test
@AllowFlaky(attempts = 5)
public void someFlakyTest() throws Exception {
// ...
}

// Use @Repeat to avoid flaky tests from passing if any repetition fails.
@Test
@Repeat(times = 5)
public void someImportantTest() throws Exception {
// ...
}

In conclusion, Barista seems like a fine alternative to add more readability and simplify tests. Since it’s built on top of Espresso, there is full compatibility, and Espresso can still be used if a complex use case is needed. Barista adds no complexity because it’s made of just static functions that call directly the Espresso. There’s really no risk in using it.

For more information:

--

--