Espresso with a Jetpack: UI-Testing Apps built with Jetpack Compose

espresso and jetpack

17 April 2023 Stephan Petzl Leave a comment Tech-Help

Jetpack compose changed how we build and test Android apps quite a bit. In this blog post, we collect challenging questions around espresso, UI testing and jetpack compose.


How to test entering text in Jetpack compose TextField?

Let’s say you want to fill a jetpack compose text field and test whether the value was inserted.
How do we do that? Well, first, take care that you have assigned a test-tag modifier, like this:

<span class="token keyword">const</span> <span class="token keyword">val</span> TAG_FIELD1 <span class="token operator">=</span> <span class="token string">"field1tag"</span>

<span class="token function">TextField</span><span class="token punctuation">(</span>
    value <span class="token operator">=</span> textState<span class="token punctuation">.</span>value<span class="token punctuation">,</span>
    modifier <span class="token operator">=</span> Modifier<span class="token punctuation">.</span><span class="token function">fillMaxWidth</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">testTag</span><span class="token punctuation">(</span>TAG_FIELD1<span class="token punctuation">)</span><span class="token punctuation">,</span>
    onValueChange <span class="token operator">=</span> <span class="token punctuation">{</span>
        textState<span class="token punctuation">.</span>value <span class="token operator">=</span> it
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    keyboardOptions <span class="token operator">=</span> <span class="token function">KeyboardOptions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span>
Kotlin

Then, in your test case you can access the field like this to set a value and then check it:

<span class="token annotation builtin">@Test</span>
<span class="token keyword">fun</span> <span class="token function">setAndCheckTheTextFieldValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    ActivityScenario<span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span>MainActivity<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span>
    <span class="token keyword">val</span> result <span class="token operator">=</span> <span class="token string">"result"</span>

    <span class="token comment">// Sets the TextField value</span>
    composeTestRule<span class="token punctuation">.</span><span class="token function">onNodeWithTag</span><span class="token punctuation">(</span>TAG_FIELD1<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">performTextInput</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span>

    <span class="token comment">// Asserts the TextField has the corresponding value</span>
    composeTestRule<span class="token punctuation">.</span><span class="token function">onNodeWithTag</span><span class="token punctuation">(</span>TAG_FIELD1<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">assert</span><span class="token punctuation">(</span><span class="token function">hasText</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
Kotlin

But let’s say there is no way for you to modify the app source. In this case, it’s also possible to access a compose field via the label instead of a test tag:

<span class="token function">TextField</span><span class="token punctuation">(</span>
  <span class="token operator">..</span><span class="token punctuation">.</span>
  label <span class="token operator">=</span> <span class="token string">"First name"</span><span class="token punctuation">,</span>
  <span class="token operator">..</span><span class="token punctuation">.</span>
<span class="token punctuation">)</span>
Kotlin

In your test, assert the field like this:

composeTestRule
  <span class="token punctuation">.</span><span class="token function">onNodeWithText</span><span class="token punctuation">(</span><span class="token string">"First name"</span><span class="token punctuation">,</span> useUnmergedTree <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span> 
  <span class="token punctuation">.</span><span class="token function">onParent</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">performTextInput</span><span class="token punctuation">(</span><span class="token string">"Tom"</span><span class="token punctuation">)</span>
Kotlin

How to test an app with Espresso, that has bits of Jetpack Compose?

You might have an app that is built with the standard Android SDK, but has a couple of fragments that are built with Jetpack Compose.
Your first approach might be to create a ComposeTestRule, but it will generate a blank ComponentActivity and display an empty screen.

The problem

<span class="token annotation builtin">@get:Rule</span>
<span class="token keyword">val</span> composeTestRule <span class="token operator">=</span> <span class="token function">createComposeRule</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
Kotlin

This is a problem. You want to integrate with an existing Espresso test, so this approach won’t be suitable.

Alternatively, you could use createAndroidComposeRule(), but this will also rely on an ActivityTestRule underneath and launch the Activity, potentially altering the behavior of your existing test. 🤔

<span class="token annotation builtin">@get:Rule</span>
<span class="token keyword">val</span> composeTestRule <span class="token operator">=</span> createAndroidComposeRule<span class="token operator"><</span>MyActivity<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
Kotlin

The solution

The solution is to use createEmptyComposeRule() . It will keep the Espresso test the same way it is, but also interact with some compose elements:

<span class="token annotation builtin">@get:Rule</span>
<span class="token keyword">val</span> composeTestRule <span class="token operator">=</span> <span class="token function">createEmptyComposeRule</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
Kotlin

Alternative approach

You can also use a framework-agnostic mobile test automation tool such as Repeato to get your job done. Repeato does not care about which technology you used to built your app.

Check out our getting-started video to get an idea of how it’s done.

Like this article? there’s more where that came from!