Testing in Compose
Tags: android, computerscience, kotlin, programming
Published: 2021-08-21 06:57:16

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

<!-- wp:paragraph -->
<p>We will cover briefly:</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Testing in Jetpack Compose</li><li>(Optional) Screenshot testing in Compose</li></ol>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: This article assumes the reader knows about Jetpack Compose</p></blockquote>
<!-- /wp:quote -->

{% youtube x6ef3BZsPOQ %}

<!-- wp:heading {"level":3} -->
<h3>Testing in Jetpack Compose</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Compose provides a set of testing APIs to find elements, verify their attributes and perform user actions.&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In our previous articles, we managed to create our <a href="https://github.com/AseemWangoo/hands_on_kotlin" rel="noreferrer noopener" target="_blank"><em>TodoApp</em></a><em>. </em>Now, let's write some tests.&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>Setup</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>Install the dependencies inside <code>build.gradle</code> of your app</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>androidTestImplementation "androidx.compose.ui:ui-test-junit4:$version"

debugImplementation "androidx.compose.ui:ui-test-manifest:$rootProject.composeVersion"</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li>Create a file called <a href="https://github.com/AseemWangoo/hands_on_kotlin/blob/master/app/src/androidTest/java/com/aseemwangoo/handsonkotlin/compose/TodoComposeTest.kt" rel="noreferrer noopener" target="_blank">TodoComposeTest</a> and add the <strong>ComposeTestRule </strong>as&nbsp;</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@get:Rule
val composeTestRule = createComposeRule()</code></pre>
<!-- /wp:code -->

<!-- wp:separator {"className":"is-style-dots"} -->
<hr class="wp-block-separator is-style-dots"/>
<!-- /wp:separator -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Compose comes with a <code>ComposeTestRule</code> which can be obtained by calling <code>createComposeRule()</code>. This rule lets you set the Compose content under test and interact with it.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>There are three main ways to interact with elements:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li><strong>Finders</strong> let you select one or multiple elements (or <em>nodes</em> in the Semantics tree) to make assertions or perform actions on them.</li><li><strong>Assertions</strong> are used to verify that the elements exist or have certain attributes.</li><li><strong>Actions</strong> inject simulated user events on the elements, such as clicks or other gestures.</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>We can simplify things considerably by testing a component in isolation. You can choose what Compose UI content to use in the test. This is done with the <code>setContent</code> method of<code>ComposeTestRule</code>&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Finding UI elements, checking their properties, and performing actions are done through the test rule, following this pattern:</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>composeTestRule{.finder}{.assertion}{.action}</code></pre>
<!-- /wp:code -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*f5AR1zIn7gIE_Y0zcL9BeA.png" alt="Testing in Compose"/><figcaption>Our TodoApp</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>Test1: testIfTitleExists</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We set the content screen having the title. In our case, we set it to HomeView using the <code>setContent</code> method of<code>ComposeTestRule</code></li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@Before
fun setUp() {
  navController = TestNavHostController(ApplicationProvider.getApplicationContext())
}
@Test
fun testIfTitleExists() {
  composeTestRule.setContent {
      HomeView(navController)
  }
composeTestRule.onNodeWithText("My ToDo List").assertIsDisplayed()
}</code></pre>
<!-- /wp:code -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Since our views have a navigation controller as a required parameter, we make use of <code>TestNavHostController</code> that exposes navigation APIs for testing.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:list -->
<ul><li>Next, we find the node with the text (as specified in the brackets), using <code>onNodeWithText</code>&nbsp;</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p><strong>OnNodeWithText</strong>: Finds a semantics node with the given text.</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Finally, using the assertion <code><a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction#%28androidx.compose.ui.test.SemanticsNodeInteraction%29.assertIsDisplayed%28%29" rel="noreferrer noopener" target="_blank">assertIsDisplayed</a></code> we make sure the text is present on the screen.</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":4} -->
<h4>Test2: testForAddTodoClick</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We set the content screen to NavigationComponent, which is the parent component of our UI.</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@Test
fun testForAddTodoClick() {
 composeTestRule.setContent {
     NavigationComponent()
 }
composeTestRule.onNodeWithText("Add Todo").performClick()
composeTestRule.onNodeWithText("Save Todo").assertExists()
}</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li>Now, we find the text node, and if found we perform the click action on it.</li><li>On clicking of <code>Add Todo</code> it takes us to the new screen. On this new screen, we have a text <code>Save Todo</code> </li><li>We verify if our test can find this node using <code><a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction#assertExists%28kotlin.String%29" rel="noreferrer noopener" target="_blank">assertExists</a></code> </li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*0H9IpgcR_54AxQyVWj_2Ow.png" alt="Testing in Compose"/><figcaption>Add Todo&nbsp;Screen</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":4} -->
<h4>Test3: testForNotSavingTodo</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We set the content screen to NavigationComponent.</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@Test
fun testForAddTodoClick() {
 composeTestRule.setContent {
     NavigationComponent()
 }
 composeTestRule.onNodeWithText("Add Todo").performClick()
 composeTestRule.onNodeWithText("Save Todo").assertExists()
 Espresso.pressBack()
 composeTestRule.onNodeWithText("My ToDo List").assertExists()
}</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li>We find the text node, and if found we perform the click action on it.</li><li>On clicking of <code>Add Todo</code> it takes us to the new screen. On this new screen, we have a text <code>Save Todo</code>We check if our test can find this node using <code><a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction#assertExists%28kotlin.String%29" rel="noreferrer noopener" target="_blank">assertExists</a></code>&nbsp;</li><li>Now, we simulate the back button press on the device using <a href="https://developer.android.com/training/testing/espresso" rel="noreferrer noopener" target="_blank">Espresso</a>. Add it to your app <code>build.gradle</code>&nbsp;</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li>We come back to the home screen and verify if the text node <code>My ToDo List</code> exists.</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":4} -->
<h4>Test4: testForTodoInputField</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We set the content screen to AddView since this view contains our todo input field.</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@Test
fun testForAddTodoClick() {
 composeTestRule.setContent {
     AddView(navController)
 }
 val tag = "InputTodo Tag" 
 val dummyText = "Dummy Text"
 
 composeTestRule.onNodeWithTag(tag).performTextInput(dummyText)
 composeTestRule.onNodeWithTag(tag).assertTextEquals(dummyText)
}</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li>For identifying our input field, we associate a tag with it. This tag is the same as when we defined our input field.</li><li>We make use of <code>onNodeWithTag</code> which helps in finding a semantics node by the given tag. Once found, we use <code><a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction#%28androidx.compose.ui.test.SemanticsNodeInteraction%29.performTextInput%28kotlin.String%29" rel="noreferrer noopener" target="_blank">performTextInput</a></code> to enter the dummy text.</li><li>Finally, we compare if the text entered in the input field is the same as the assertion, using <code><a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction#%28androidx.compose.ui.test.SemanticsNodeInteraction%29.assertTextEquals%28kotlin.Array,kotlin.Boolean%29" rel="noreferrer noopener" target="_blank">assertTextEquals</a></code>&nbsp;</li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*qfW0RWDPiUxrIqwmFhJ-cA.png" alt="Testing in Compose"/><figcaption>Test Results</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3>Screenshot testing in&nbsp;Compose</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>In this section, we will see how to perform screenshot tests. We will be testing our AddView screen (see the screenshot above).&nbsp;</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>We take a screenshot of the screen, by running the test and letting it fail. This creates a screenshot for us, present inside the app’s cache folder.</li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*dhViU4siV1iHXsDA7ZQRsw.png" alt="Testing in Compose"/><figcaption>Screenshot saved in&nbsp;Cache</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Next, we move this file (renamed it to add_todo) and place it under our assets folder <em>(inside AndroidTest).</em></li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*vfbp3LvgAsywFy39Eweeog.png" alt="Testing in Compose"/><figcaption>File in&nbsp;assets</figcaption></figure></div>
<!-- /wp:image -->

<!-- wp:list -->
<ul><li>Create a file called <a href="https://github.com/AseemWangoo/hands_on_kotlin/blob/master/app/src/androidTest/java/com/aseemwangoo/handsonkotlin/compose/ScreenshotComparatorTest.kt" rel="noreferrer noopener" target="_blank">ScreenshotComparatorTest</a> and add the <strong>ComposeTestRule </strong>as</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@get:Rule<br>val composeTestRule = <em>createComposeRule</em>()</pre>
<!-- /wp:preformatted -->

<!-- wp:heading {"level":4} -->
<h4>Test1: testAddTodoScreen</h4>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>We set the content screen to AddView since we are comparing the above screenshot with it.</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>@Before
fun setUp() {
  navController = TestNavHostController(ApplicationProvider.getApplicationContext())
}
@Test
fun testAddTodoScreen() {
  composeTestRule.setContent {
     AddView(navController)
  }
 assertScreenshotMatchesGolden("add_todo", composeTestRule.onRoot())
}</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li><code>assertScreenshotMatchesGolden</code> is our custom function, which takes in the name of the golden file (<em>add_todo</em>) and compares to the current composable screen using <code>composeTestRule.onRoot</code>&nbsp;</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>As per the docs for <code>composeTestRule.onRoot</code>&nbsp;:</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Finds the root semantics node of the Compose tree. Useful for screenshot tests of the entire scene.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:list -->
<ul><li>Inside the <code>assertScreenshotMatchesGolden</code> , we capture the current screen as a bitmap, and then we save the image as a file with a name and current timestamp in milliseconds.</li><li>This file is saved under the app’s cache folder (see the above screenshot)</li></ul>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>fun assertScreenshotMatchesGolden(goldenName: String,node:  SemanticsNodeInteraction) {
  val bitmap = node.captureToImage().asAndroidBitmap()
 saveScreenshot(goldenName + System.currentTimeMillis().toString(),  bitmap)
        
val golden = InstrumentationRegistry.getInstrumentation()
            .context.resources.assets.open("$goldenName.png").use {   BitmapFactory.decodeStream(it) }

// This function does the comparison
golden.compare(bitmap)
}</code></pre>
<!-- /wp:code -->

<!-- wp:list -->
<ul><li>Using <code><a href="https://developer.android.com/reference/androidx/test/platform/app/InstrumentationRegistry" rel="noreferrer noopener" target="_blank">InstrumentationRegistry</a></code> we get access to the low-level APIs, which in our case are the assets.&nbsp;</li><li>Finally, we open the screenshot captured by the test and compare it with the asset image (which we saved on the initial run of this test)</li></ul>
<!-- /wp:list -->

<!-- wp:image {"align":"center"} -->
<div class="wp-block-image"><figure class="aligncenter"><img src="https://cdn-images-1.medium.com/max/1600/1*IsBBHvPsSOHcs0j4RFmlQg.png" alt="Testing in Compose"/><figcaption>Screenshot Comparator Test</figcaption></figure></div>
<!-- /wp:image -->

{% youtube x6ef3BZsPOQ %}

<!-- wp:paragraph -->
<p><a rel="noreferrer noopener" href="https://github.com/AseemWangoo/hands_on_kotlin" target="_blank"><em>Source code.</em></a>

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>