Learning Android Development

Testing Composable in Jetpack Compose Android - Testing Fundamentals

Don’t judge a UI by its cover!

Saurabh Pant
Mobile App Development Publication

--

Source: https://blog.testproject.io/

In the world of Android applications, Jetpack Compose is the latest practice followed for UI development. While it is declarative, it sometimes gets complicated because whatever you designed is all code. Additionally, we’ve so many states to maintain in Composables. Hence, writing test cases for UI becomes very critical and important.

You can watch my short course on testing composable at

https://academy.droidcon.com/course/testing-composables-in-android-jetpack-compose

In this article, we’ll get started with writing tests for our composable. We’ll see what all apis are available to do so and how can we think of use cases on our UIs so that we can test them correctly.

We’ll cover the topic in the following order

  • Creating a Composable with inputs and actions
  • Setting up the test environment
  • Writing test cases

So, let’s get started!

Creating a Composable with inputs and actions

Firstly, let’s create a composable which looks like the below image.

Composable to be tested

Shown above are the three states of our composable.

  1. Initial state: Once we launch our activity, the first image would be our initial state with Start copying as the default text placed until we perform any click action on the Copy button. TextField remains empty with Input as a label.
  2. Invalid state: The user can enter any input text but we’re checking it for Int type. So, if the input is not a number and the user clicks on the Copy button then we’ll show an Invalid entry in place of the Start copying label.
  3. Valid state: If the user enters any number then we’ll show the entered input along with text Counter = $input on click on Copy Button.

So let’s quickly create the composable as creating a composable is not the main focus area for this article.

We’ll have a Text at the top, then a TextInput field and a Button, all wrapped under a column.

Previewing the composable, we’ll see it below.

Cool. We are ready with our UI now. Let’s now set up its test environment.

Setting up the test environment

By default, the dependencies we need, are provided when we create our project. In case they’re not present in your build.gradle then do add them.

androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"

We should also make sure that we’ve set our test runner as AndroidJUnitRunner in the default config in build.gradle. This is important because AndroidJUnitRunner provide us support to perform unit testing under an Android environment.

defaultConfig {
...

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...
}

Ok. Let’s now create the test class for our composable.

Remember that this class is to be created under androidTest folder and not in test folder.

Once our class is created, we annotate it with RunWith(AndroidJUnit4::class) to indicate that the tests are to be run with an Android JUnit runner and not with a regular JUnit runner.

We now need to create a compose rule object which is the main key to testing our composable. createComposeRule is required because it provides us access to our composable components. With its help, we can even test our composable in isolation.

In case we want our activity reference in testing then we can create createAndroidComposeRule<MainActivity>().

We then set our composable as content to our compose rule.

So our test class looks like this.

Alright! We’re now done with our setup and can start writing our test cases.

Writing test cases

Whoo! Time to put everything into action. Let’s test if the three states we mentioned during UI creation, are correctly shown when the specific action is performed on clicking the Copy button.

Test Case 1: Initial state

At first, we simply verify if all of our views in the default state exist or not.

Let’s quickly see how we access our composable components here.

Line 3: With composeTestRule.onNodeWithTag indicates that a component with testing tag as “Counter Display” exists or not.

Line 4: With composeTestRule.onNodeWithTag indicates that a component with testing tag as “Input” exists or not.

Line 5: With composeTestRule.onNodeWithText indicates that a component with text “Start copying” on it exists or not.

onNodeWithText and onNodeWithTag are of the same purpose here but we can use either. We can set a testTag to our modifier so that we can identify the component when testing.

Running the test would result as follows:

Test Case 2: Invalid state

If we perform a click action on the Copy button while the input is empty the display text should be an Invalid entry. This is because our helper function returns this text when a number format exception occurs.

Line 3: we first identify our view with text Copy on it. Once identified, we perform a click action on it. Now the onClick block of our button will be called.

Line 5: We know that if the input is invalid it would be displayed as an Invalid entry. So we first identify our view with test tag “Counter Display” where the final output will be displayed. Then we assert if the text on it is “Invalid entry” or not.

Running the test will result as follows.

Test Case 3: Valid state

Now, we’ll test if we correctly input an integer and click on the button, do we get the text as “Counter = 1” on the “Display Counter” text component?

Running the test will result as follows.

Running all three test cases together will result as follows.

Great! Superb! We’ve finally tested our composable for all the different scenarios and verified that they behave as expected. Now we’re more confident on our UI part.

You can play around by writing more such scenarios and tests. This would be fun. Getting so far would be enough to get started on testing the compostables. We’ll test more complex UI once we’re good at testing the simple ones.

Check out the complete code for this article below.

Bonus Read

In case you want to read about testing various layers of your app. Do check out the below links:

That is all for now! Stay tuned!

Connect with me on medium(if the content is helpful to you) or on GitHub and subscribe to email to be in sync for further interesting topics on Android/IOS/Backend/Web.

Until next time…

Cheers!

--

--

Saurabh Pant
Mobile App Development Publication

App Developer (Native & Flutter) | Mentor | Writer | Instructor @Droidcon | Youtube: https://www.youtube.com/@_zaqua | Publication: https://medium.com/zaqua