← Back to list

Testing in Jetpack Compose — A Beginner's Guide

Introduction

Anuradha Singh · 2026-06-12 12:49 · 8 claps · 3.7 min read
#android #jetpack-compose #kotlin #android-development #programming
Open on Medium ↗
Wiki topics: 💻 · Programming 📱 · Mobile Development

Testing in Jetpack Compose — A Beginner's Guide

Introduction

You have built a beautiful Compose app with proper architecture, navigation, animations and performance. But how do you make sure everything keeps working as your app grows?

The answer is Testing!

Most developers skip testing because it feels complicated and time consuming. But in Jetpack Compose, testing is actually fun and easy once you know the basics!

In this beginner-friendly guide, I will walk you through everything you need to know about testing in Jetpack Compose — with real examples you can use right away!

Why Testing Matters?

  • Catches bugs before users do 🐛
  • Makes refactoring safe and confident 💪
  • Saves hours of manual testing time ⏰
  • Makes your code more reliable
  • Impresses in job interviews 🎯

Types of Tests in Android

┌─────────────────────────────────────┐ │ UI Tests (Compose) │ ← Test what user sees ├─────────────────────────────────────┤ │ Integration Tests │ ← Test layers together ├─────────────────────────────────────┤ │ Unit Tests │ ← Test single functions └─────────────────────────────────────┘

Step 1 — Add Dependencies

dependencies { // Unit Testing testImplementation “junit:junit:4.13.2” testImplementation “org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3” testImplementation “io.mockk:mockk:1.13.8”

// Compose UI Testing androidTestImplementation “androidx.compose.ui:ui-test-junit4:1.5.4” androidTestImplementation “androidx.test.ext:junit:1.1.5” androidTestImplementation “androidx.test.espresso:espresso-core:3.5.1”

// Hilt Testing androidTestImplementation “com.google.dagger:hilt-android-testing:2.48” kaptAndroidTest “com.google.dagger:hilt-android-compiler:2.48”

debugImplementation “androidx.compose.ui:ui-test-manifest:1.5.4” }

Part 1 — Unit Tests

Unit tests test single functions or classes in isolation — no Android device needed!

Testing a ViewModel:

// ViewModel to test
@HiltViewModel
class CounterViewModel @Inject constructor() : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count.asStateFlow()
   fun increment() { _count.value++ }
    fun decrement() { _count.value-- }
    fun reset() { _count.value = 0 }
}

Unit Test:

class CounterViewModelTest {
  private lateinit var viewModel: CounterViewModel
 @Before
    fun setup() {
        viewModel = CounterViewModel()
    }
 @Test
    fun `initial count should be zero`() {
        assertEquals(0, viewModel.count.value)
    }
  @Test
    fun `increment should increase count by 1`() {
        viewModel.increment()
        assertEquals(1, viewModel.count.value)
    }
 @Test
    fun `decrement should decrease count by 1`() {
        viewModel.increment()
        viewModel.decrement()
        assertEquals(0, viewModel.count.value)
    }
  @Test
    fun `reset should set count to zero`() {
        viewModel.increment()
        viewModel.increment()
        viewModel.reset()
        assertEquals(0, viewModel.count.value)
    }
}

Testing a UseCase:

// UseCase to test
class GetArticlesUseCase @Inject constructor(
    private val repository: NewsRepository
) {
    suspend operator fun invoke(): Result<List<Article>> {
        return repository.getArticles()
    }
}

UseCase Test with MockK:

class GetArticlesUseCaseTest {
    private val repository: NewsRepository = mockk()
    private lateinit var useCase: GetArticlesUseCase
   @Before
    fun setup() {
        useCase = GetArticlesUseCase(repository)
    }
    @Test
    fun `returns articles on success`() = runTest {
        // Given
        val fakeArticles = listOf(
            Article("1", "Title 1", "Desc 1", ""),
            Article("2", "Title 2", "Desc 2", "")
        )
        coEvery { repository.getArticles() } returns Result.success(fakeArticles)
   // When
        val result = useCase()
  // Then
        assertTrue(result.isSuccess)
        assertEquals(2, result.getOrNull()?.size)
    }
 @Test
    fun `returns error on failure`() = runTest {
        // Given
        coEvery { repository.getArticles() } returns
            Result.failure(Exception("Network error"))
  // When
        val result = useCase()
 // Then
        assertTrue(result.isFailure)
        assertEquals("Network error", result.exceptionOrNull()?.message)
    }
}

Part 2 — Compose UI Tests

Compose UI tests let you interact with your UI and verify what is shown on screen.

Setting Up Compose Test Rule:

class MyComposeTest {
@get:Rule
    val composeTestRule = createComposeRule()
}

Testing a Simple Composable:

// Composable to test
@Composable
fun GreetingScreen(name: String) {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Hello, $name!",
            fontSize = 24.sp
        )
        Button(onClick = {}) {
            Text("Click Me")
        }
    }
}

UI Test:

class GreetingScreenTest {
 @get:Rule
    val composeTestRule = createComposeRule()
  @Test
    fun greetingScreen_showsCorrectName() {
        // Set content
        composeTestRule.setContent {
            GreetingScreen(name = "Anuradha")
        }
  // Check text is displayed
        composeTestRule
            .onNodeWithText("Hello, Anuradha!")
            .assertIsDisplayed()
    }
  @Test
    fun greetingScreen_buttonIsDisplayed() {
        composeTestRule.setContent {
            GreetingScreen(name = "Anuradha")
        }
.onNodeWithText("Click Me")
            .assertIsDisplayed()
    }
}

Testing User Interactions:

// Composable to test
@Composable
fun CounterScreen() {
    var count by remember { mutableStateOf(0) }
 Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(
            text = "Count: $count",
            modifier = Modifier.testTag("countText")
        )
        Button(
            onClick = { count++ },
            modifier = Modifier.testTag("incrementButton")
        ) {
            Text("Increment")
        }
        Button(
            onClick = { count-- },
            modifier = Modifier.testTag("decrementButton")
        ) {
            Text("Decrement")
        }
    }
}

Interaction Test:

class CounterScreenTest {
    @get:Rule
    val composeTestRule = createComposeRule()
   @Test
    fun counterScreen_incrementsCorrectly() {
        composeTestRule.setContent {
            CounterScreen()
        }
    // Initial state
        composeTestRule
            .onNodeWithTag("countText")
            .assertTextEquals("Count: 0")
  // Click increment
        composeTestRule
            .onNodeWithTag("incrementButton")
            .performClick()
 // Verify count increased
        composeTestRule
            .onNodeWithTag("countText")
            .assertTextEquals("Count: 1")
    }
 @Test
    fun counterScreen_decrementsCorrectly() {
        composeTestRule.setContent {
            CounterScreen()
        }
 // Click increment then decrement
        composeTestRule.onNodeWithTag("incrementButton").performClick()
        composeTestRule.onNodeWithTag("decrementButton").performClick()
  // Verify count is back to 0
        composeTestRule
            .onNodeWithTag("countText")
            .assertTextEquals("Count: 0")
    }
}

Part 3 — Testing with ViewModel

class NewsScreenTest {
 @get:Rule
    val composeTestRule = createComposeRule()
 @Test
    fun newsScreen_showsLoadingIndicator() {
        val fakeUiState = NewsUiState(isLoading = true)
 composeTestRule.setContent {
            NewsScreenContent(uiState = fakeUiState)
        }
 composeTestRule
            .onNodeWithTag("loadingIndicator")
            .assertIsDisplayed()
    }
@Test
    fun newsScreen_showsArticles() {
        val fakeArticles = listOf(
            Article("1", "Test Article 1", "Description 1", ""),
            Article("2", "Test Article 2", "Description 2", "")
        )
        val fakeUiState = NewsUiState(articles = fakeArticles)
 composeTestRule.setContent {
            NewsScreenContent(uiState = fakeUiState)
        }
  composeTestRule
            .onNodeWithText("Test Article 1")
            .assertIsDisplayed()
  composeTestRule
            .onNodeWithText("Test Article 2")
            .assertIsDisplayed()
    }
 @Test
    fun newsScreen_showsErrorMessage() {
        val fakeUiState = NewsUiState(errorMessage = "Network error")
 composeTestRule.setContent {
            NewsScreenContent(uiState = fakeUiState)
        }
  composeTestRule
            .onNodeWithText("Network error")
            .assertIsDisplayed()
    }
}

Most Useful Compose Test APIs

// Finding nodes
onNodeWithText("Hello")          // find by text
onNodeWithTag("myTag")           // find by test tag
onNodeWithContentDescription("") // find by content description
// Assertions
.assertIsDisplayed()             // is visible on screen
.assertIsEnabled()               // is clickable
.assertIsSelected()              // is selected
.assertTextEquals("text")        // exact text match
.assertExists()                  // exists in tree
// Actions
.performClick()                  // tap
.performTextInput("hello")       // type text
.performScrollTo()               // scroll to node
.performTouchInput { swipeLeft() } // swipe

Quick Reference — What to Test

If this article helped you, please clap 👏 and follow for more Jetpack Compose content. See you in the next one!


메타데이터
post_id
41f435169334
slug
testing-in-jetpack-compose-a-beginners-guide-41f435169334
url
https://medium.com/@anusingh9117/testing-in-jetpack-compose-a-beginners-guide-41f435169334
canonical_url
https://medium.com/@anusingh9117/testing-in-jetpack-compose-a-beginners-guide-41f435169334
author_url
https://medium.com/@anusingh9117
status
ok
fetched_at
2026-06-13 12:55:53