Testing on Android.

Code should work correctly. Tests verify that it does. We have learned to build apps. Now we learn to test them. Today we explore testing strategies for Android applications.

Tests catch bugs before users do.

They give confidence to refactor. Change code, run tests. If they pass, you did not break anything.

Tests document behavior. Reading tests shows how code should work.

Tests save time. Manual testing is slow. Automated tests run in seconds.

Tests are especially important in Android. Many devices, many OS versions. You cannot manually test everything.

Unit tests run on your computer. Fast. No device needed.

They test small units of code. A function. A class. Isolated from Android framework.

Instrumented tests run on a device or emulator. Slower. Need Android environment.

They test code that interacts with Android. UI components. Database operations. Real device behavior.

Start with unit tests. They are fast and cheap. Add instrumented tests for Android-specific code.

ViewModels are ideal for unit testing.

They contain logic. They transform data. They do not touch Android UI directly.

Create the ViewModel with fake dependencies. Call methods. Assert state.

class UsersViewModelTest at Test fun loadUsers updates state viewModel dot loadUsers assertEquals expected viewModel dot uiState dot value.

Mock or fake the Repository. Control what data it returns. Test ViewModel behavior.

Coroutines need special handling in tests.

Use runTest instead of runBlocking. It handles virtual time. Tests do not wait for delays.

runTest brace val viewModel equals... viewModel dot loadData advanceUntilIdle assertEquals expected viewModel dot state close brace.

advanceUntilIdle runs all pending coroutines. No waiting.

Use TestDispatcher for controlled execution. Replace Dispatchers in tests.

Standard coroutine testing tools make async code testable.

Test doubles replace real dependencies.

A fake is a working implementation for tests. FakeUserRepository stores data in memory. It behaves like a real repository.

class FakeUserRepository colon UserRepository private val users equals mutableListOf override suspend fun getUsers equals users.

A mock records interactions. You verify methods were called. Mockito or MockK create mocks.

Fakes are often better. They test behavior, not implementation details. Tests are less brittle.

Compose has a testing library.

Use createComposeRule to set up tests.

at get Rule val composeTestRule equals createComposeRule.

Set content and interact. composeTestRule dot setContent brace MyComposable close brace.

Find elements with matchers. onNodeWithText "Submit" dot performClick.

Assert state. onNodeWithText "Success" dot assertIsDisplayed.

Compose tests verify UI renders correctly and responds to interaction.

Test pyramid: many unit tests, fewer integration tests, even fewer UI tests.

Unit tests are fast and focused. Test logic in isolation.

Integration tests verify components work together. ViewModel with Repository.

UI tests verify the whole screen works. Slower but important.

Cover critical paths with UI tests. Cover edge cases with unit tests.

Balance coverage with speed. Fast feedback is valuable.

Test behavior, not implementation. Focus on inputs and outputs.

Keep tests independent. Each test sets up its own state.

Name tests clearly. shouldShowErrorWhenNetworkFails.

Test edge cases. Empty lists. Error states. Boundary values.

Run tests often. After each change. Catch regressions early.

Make tests deterministic. No random data. No time dependencies.

Let us summarize.

Unit tests run fast, no device needed. Instrumented tests verify Android behavior.

Test ViewModels with fake dependencies.

Use runTest for coroutine testing.

Fakes often work better than mocks.

Compose UI can be tested with composeTestRule.

Follow the test pyramid. Many unit tests, fewer UI tests.

Testing builds confidence in your code.