Android Unit Testing Excellence 🧪🛡️
The ultimate guide to writing robust, reliable, and maintainable unit tests for Android applications using modern tools like MockK, Turbine, and Coroutine Test.
⚡ When to Use
- Business Logic Verification: Testing Use Cases or ViewModels.
- Data Layer Testing: Testing Repositories or Data Sources (with Fakes).
- Reactive Stream Testing: Verifying emitted values from
FlowandStateFlow. - Logic Refactoring: Ensuring no regressions during code changes.
- TDD (Test-Driven Development): Writing tests before implementation.
🏗️ Core Tools
1. JUnit 5 (Modern Standard)
- Use
@Test,@BeforeEach, and@DisplayNamefor clear tests. - Rule: Prefer JUnit 5 for new projects, but JUnit 4 is common in legacy.
2. MockK (Power Mocking)
- Rule: Use MockK for Kotlin-first mocking.
- Shorthand:
every { ... } returns ...andcoEvery { ... } returns ...for suspend functions.val repository = mockk<UserRepository>() coEvery { repository.getUser("123") } returns User("John")
3. Turbine (Flow Testing)
- Rule: Never use
collectorfirst()in tests manually. Use Turbine.flow.test { assertEquals(1, awaitItem()) assertEquals(2, awaitItem()) awaitComplete() }
🚀 Coroutine Testing (Essentials)
Use runTest
runTestskipsdelay()and ensures tests run fast and predictably.
Inject Dispatchers
- Rule: NEVER hardcode
Dispatchers.IOorMain. Inject them. - In Tests: Use
StandardTestDispatcherorUnconfinedTestDispatcher.
🧪 Testing Patterns
1. ViewModels
- Standard: Test the Initial State → Trigger Action → Verify Final State and/or Side Effects.
- Note: Ensure
Dispatchers.setMain(testDispatcher)is called before andDispatchers.resetMain()after tests.
2. Repositories
- Standard: Test that data is correctly mapped and error handling is resilient.
- Fakes: Use
FakeLocalSourceorFakeRemoteSourcefor complex integration tests.
3. Domain Use Cases
- Standard: Test that the use case calls the repository and applies business rules correctly.
📦 Assertion Libraries
- Truth (from Google): Fluent, readable assertions.
- AssertJ: Extremely powerful and comprehensive.
- Kotest Assertions: Native Kotlin syntax (e.g.,
user.name shouldBe "John").
🚀 Testing Checklist
- Method Names: Use descriptive names like
test_whenQueryChanged_updatesState. - One Assertion per Test: (Ideally) One logical assertion per test for better failure isolation.
- Arrange-Act-Assert (AAA): Follow this structure for readability.
- Verify Mocks: Check if expected methods were called using
verify { ... }.