Android Testing Guide
JUnit5, MockK, Turbine, Compose UI Testing을 사용한 테스트 가이드입니다.
Testing Stack
| Type | Framework | Purpose |
|---|---|---|
| Unit | JUnit5 + MockK | ViewModel, UseCase, Repository |
| Flow | Turbine | StateFlow, Channel testing |
| Coroutines | kotlinx-coroutines-test | runTest, advanceUntilIdle |
| UI | Compose Testing | Composable rendering, interaction |
| Integration | Hilt Testing | DI integration |
Coverage Target
- •Overall: 80%+
- •Domain Layer: 90%+ (pure business logic)
- •Presentation Layer: 70%+ (ViewModel, state logic)
- •Data Layer: 60%+ (repository, mapper)
Quick Test Commands
bash
# All tests ./gradlew test # Module tests ./gradlew :feature:home:testDebugUnitTest ./gradlew :core:domain:test # With coverage ./gradlew testDebugUnitTestCoverage # Single test class ./gradlew test --tests "*.HomeViewModelTest"
Test Templates
테스트 함수 이름은 되도록 한국어로 작성 해주세요.
ViewModel Test
ViewModel 테스트는 state 변화와 effect 발생을 검증합니다.
상세 템플릿: templates/viewmodel-test.md
UseCase Test
UseCase 테스트는 비즈니스 로직과 repository 호출을 검증합니다.
상세 템플릿: templates/usecase-test.md
Compose UI Test
Compose 테스트는 UI 렌더링과 사용자 상호작용을 검증합니다.
상세 템플릿: templates/compose-test.md
Test Naming Convention
kotlin
// Pattern: `given_when_then` or `should_when` @Test fun `loadData should update state with results when useCase succeeds`() @Test fun `should show error message when loading fails`() @Test fun `given empty list when render then show empty state`()
Common Test Setup
Dependencies (build.gradle.kts)
kotlin
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
testImplementation("io.mockk:mockk:1.13.8")
testImplementation("app.cash.turbine:turbine:1.0.0")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
testImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-test-manifest")
Test Rule Setup
kotlin
@ExtendWith(MockKExtension::class)
class ViewModelTest {
@MockK
private lateinit var useCase: SomeUseCase
@MockK
private lateinit var repository: SomeRepository
private lateinit var viewModel: SomeViewModel
@BeforeEach
fun setup() {
viewModel = SomeViewModel(useCase)
}
@AfterEach
fun tearDown() {
clearAllMocks()
}
}
Best Practices
DO
- •Test behavior, not implementation
- •Use meaningful test names
- •One assertion per test (when possible)
- •Test edge cases and error scenarios
- •Use Turbine for Flow testing
DON'T
- •Test private methods directly
- •Mock everything (test real logic when possible)
- •Write flaky tests
- •Skip error case testing
- •Use Thread.sleep() (use advanceUntilIdle)