Skip to main content

Command Palette

Search for a command to run...

Chapter 13 - Testing the Composable UI ArtListScreen

Published
•3 min read•View as Markdown
@RunWith(AndroidJUnit4::class)
class ArtListScreenTest {

    @get:Rule
    val composableRule = createComposeRule()

    //fake repository
    private val fakeRepo = object : ArtRepositoryInterface {
        override fun getArt(): Flow<List<Art>> = flowOf(
            listOf(
                Art(id = 1, name = "Starry Horse", artistName = "Viru Pandey", year = "1904", imageUrl = "url1"),
                Art(id = 2, name = "Mano ya na mano", artistName = "Satish Shah", year = "1998", imageUrl = "url2")
            )
        )

        override fun insertArt(art: Art) {}
        override fun deleteArt(art: Art) {}
        override fun searchImage(userQuery: String): Flow<Resource<List<ImageResult>>> =
            flow { emit(Resource.Success(emptyList())) }
    }

    private val getArtUseCase = GetArtUseCase(fakeRepo)
    private val viewModel = ArtViewModel(getArtUseCase)
    private val lists = listOf(Art(1, "Starry Horse", "Viru Pandey", "1904", "url1"),
        Art(2, "Mano ya na mano", "Satish Shah", "1998", "url2"))

    // 1. Normal data case
    @Test
    fun artListScreen_displaysArtNamesFromViewModel() {
        composableRule.setContent {
            ArtListScreen(
                artList = lists
            )
        }


        composableRule.onNodeWithText("Starry Horse").assertIsDisplayed()
        composableRule.onNodeWithText("Mano ya na mano").assertIsDisplayed()
    }


    // 2. Loading state test
    @Test
    fun artListScreen_showsLoadingIndicatorWhenLoading() {
        composableRule.setContent {
            ArtListScreen(
                artList = emptyList(),
                isLoading = true
            )
        }
        composableRule.onNodeWithTag("Progress").assertExists()
    }

    // 3. Error state test
    @Test
    fun artListScreen_displaysErrorMessageWhenErrorOccurs() {
        // Option 1: Pass error directly (simplest)
        composableRule.setContent {
            ArtListScreen(
                artList = emptyList(),
                errorMessage = "Something went wrong"
            )
        }
        composableRule.onNodeWithText("Something went wrong").assertIsDisplayed()
    }

}

The above code is an instrumentation test and will run on an android device or emulator.

We test the following

  • The fake data appears on screen

  • Loading & error states behave as expected.

Test 1 : artListScreen_displaysArtListCorrectly

Purpose

Verifies that the UI displays the list of artworks correctly when provided with sample data.

Setup

  • Creates a list of two Art objects (Starry Horse and Mano ya na mano).

  • Renders the ArtListScreen composable with this list.

Assertions

  • Ensures that both artwork titles are visible on the screen using onNodeWithText().assertIsDisplayed().

  • Confirms that LazyColumn correctly renders multiple items.

Summary

  • This is a sanity UI test — checks that Compose correctly binds data to the visible layout.

  • It doesn’t depend on any repository or ViewModel — pure composable test.

Test 2 : artListScreen_showsLoadingIndicatorWhenLoading

Purpose

Validates that the progress indicator (loading spinner) is displayed when the screen is in a loading state.

Setup

  • Passes isLoading = true to ArtListScreen.

  • Uses a Modifier.testTag("Progress") for the loading indicator in the composable.

Assertions

  • Checks that a node with tag "Progress" exists on the screen (assertExists()).

Summary

  • Helps verify that UI reacts properly to loading state changes.

  • In the real app, this state will be controlled by viewModel.isLoading.

  • This is important for user feedback during data fetching.

Test 3 : artListScreen_displaysErrorMessageWhenErrorOccurs

Purpose

Checks that an error message is displayed if something goes wrong.

Setup

  • Renders ArtListScreen with errorMessage = "Something went wrong".

  • Passes an empty artList (since we’re only testing error display).

Assertions

  • Verifies that the error text is visible on the screen using onNodeWithText("Something went wrong").assertIsDisplayed().

Summary

  • Simulates the scenario where a Room query fails.

  • Confirms that the user is not left with a blank screen but sees a clear error message.

  • This test ensures that your UI reacts correctly to error state updates from the ViewModel.

FINAL SUMMARY

These three tests together verify UI reactivity — the most important principle of Jetpack Compose.
They prove that when the ViewModel updates the StateFlow (data, loading, error), the UI layer reflects those changes instantly and correctly.

The flowchart sums up the work that we have done so far.

More from this blog

Architecting Android: The Right way

31 posts

Exploring Clean Architecture in Android with Kotlin and Jetpack Compose — simplifying design patterns, testing, and app structure for curious developers.