Let's think about it this way for a second
A Unit Test checks an individual function/class (ViewModel logic, data transformation) without needing a UI/device — it uses the JUnit framework, runs fast, and can be run frequently in a CI pipeline. A UI Test (Compose Test), on the other hand, automates actual UI interactions (button clicks, text input) on an emulator/device — you can use `composeTestRule` to verify things like 'does the text change when I click this button.'
Let's connect this to a real scenario
To unit test the logic in `TodoViewModel.addTodo()`, you could write `@Test fun addTodo_increasesListSize() { viewModel.addTodo("Test"); assertEquals(1, viewModel.todos.size) }` — for a UI test, you could write `composeTestRule.onNodeWithText("Add").performClick(); composeTestRule.onNodeWithText("Test").assertIsDisplayed()` to verify that the todo item actually shows up on screen after clicking the button.
Let's walk through it together
// Unit test
class TodoViewModelTest {
@Test
fun addTodo_increasesListSize() {
val viewModel = TodoViewModel(fakeDao)
viewModel.addTodo("Buy milk")
assertEquals(1, viewModel.todos.size)
}
}
// Compose UI test
class TodoScreenTest {
@get:Rule val composeTestRule = createComposeRule()
@Test
fun clickingAddButton_showsNewTodo() {
composeTestRule.setContent { TodoScreen() }
composeTestRule.onNodeWithText("Add").performClick()
composeTestRule.onNodeWithText("Buy milk").assertIsDisplayed()
}
}$ ./gradlew test
BUILD SUCCESSFUL — 2 tests passed5-minute try-it
Write a unit test yourself for `TodoViewModel`'s `addTodo()` function, and run it with Android Studio's 'Run Test' button.
A quick word of caution
Don't chase 100% test coverage — prioritize testing critical business logic (payments, data integrity); tests for UI details (color, spacing) tend to have a low ROI.