Unit Testing
Coverage Target
| Layer | Min. Coverage |
|---|---|
| Use Cases | 90% |
| ViewModels / Cubits | 80% |
| Repositories | 80% |
| Utilities / Extensions | 70% |
| UI Components | 50% (widget test) |
Framework per Platform
| Platform | Framework | Mocking |
|---|---|---|
| Android | JUnit 5 | MockK / Mockito-Kotlin |
| iOS | XCTest / Swift Testing | Protocol-based mocking |
| Flutter | flutter_test | Mockito / Mocktail |
| React Native | Jest | Jest mocks |
What to Test
- ✅ Use cases (input → output)
- ✅ ViewModel state transitions
- ✅ Repository data mapping
- ✅ Utility functions
- ✅ Error handling paths
- ❌ Framework internals
- ❌ Simple getters/setters
Example
- Kotlin
- Dart
class GetUserUseCaseTest {
private val repository = mockk<UserRepository>()
private val useCase = GetUserUseCase(repository)
@Test
fun `should return user when repository succeeds`() = runTest {
val expected = User(id = "1", name = "John")
coEvery { repository.getUser("1") } returns Result.success(expected)
val result = useCase("1")
assertTrue(result.isSuccess)
assertEquals(expected, result.getOrNull())
}
@Test
fun `should return failure when repository fails`() = runTest {
coEvery { repository.getUser("1") } returns Result.failure(Exception("Not found"))
val result = useCase("1")
assertTrue(result.isFailure)
}
}
void main() {
late MockUserRepository mockRepository;
late GetUserUseCase useCase;
setUp(() {
mockRepository = MockUserRepository();
useCase = GetUserUseCase(mockRepository);
});
test('should return user when repository succeeds', () async {
final expected = User(id: '1', name: 'John');
when(() => mockRepository.getUser('1'))
.thenAnswer((_) async => expected);
final result = await useCase('1');
expect(result, equals(expected));
verify(() => mockRepository.getUser('1')).called(1);
});
}
Naming Convention
Test name harus mendeskripsikan behavior, bukan implementasi:
- ✅
should return user when repository succeeds - ❌
testGetUser