๐งช Unit Testing Kotlin (Android) โ Personal Notes
Catatan ini dibuat sebagai referensi cepat agar tidak perlu mencari materi ulang. Fokus pada decision making saat menulis unit test.
๐ง Fundamental Conceptโ
๐น Real Object vs Mockโ
Rule:
- Class yang diuji โ REAL
- Dependency โ MOCK
Contoh:
- Test ViewModel โ ViewModel real, UseCase mock
- Test Repository โ Repository real, DataSource mock
๐น Mock vs Spy (spyk)โ
Apa itu spyk?โ
spyk = spy object (partial mock)
Artinya:
Object asli (real object) yang tetap menjalankan behavior aslinya, tapi bisa di-override sebagian.
Perbedaanโ
| Type | Behavior |
|---|---|
mockk() | Semua fake |
spyk() | Real + sebagian bisa di-mock |
Contohโ
class Calculator {
fun add(a: Int, b: Int) = a + b
fun multiply(a: Int, b: Int) = a * b
}
val calculator = spyk(Calculator())
every { calculator.add(1, 2) } returns 10
assertEquals(10, calculator.add(1, 2)) // mocked
assertEquals(6, calculator.multiply(2, 3)) // real
๐ฏ Kapan pakai spyk?โ
Gunakan spyk kalau:
- Mau test logic asli
- Tapi ada method internal yang perlu dikontrol
Contoh:
- override API call
- override database
- tapi business logic tetap real
โ ๏ธ Rule Aman Pakai spykโ
- Mulai dengan real object
- Mock semua dependency luar
- Kalau mentok karena method internal โ baru pakai spyk
- Kalau ragu โ jangan pakai dulu
๐ซ Hindari spyk kalau:โ
- Bisa pakai mock biasa
- Test jadi sulit dipahami
- Terlalu sering dipakai (code smell)
๐ Flow & Coroutine Testingโ
๐น runTestโ
Gunakan kalau:
- Ada suspend
- Ada Flow
- Ada delay / coroutine
@Test
fun testSomething() = runTest {
val result = repository.getData()
assertEquals("OK", result)
}
๐น first() vs Turbineโ
| Case | Gunakan |
|---|---|
| Flow 1 emission | first() |
| Flow banyak emission | Turbine |
๐น Turbineโ
Gunakan untuk:
- Testing urutan state
- Multi emission
- Async flow
Contoh:
viewModel.state.test {
assertEquals(Loading, awaitItem())
assertEquals(Success, awaitItem())
}
๐ Decision Rules (Cheat Sheet)โ
1. Real vs Mockโ
- Object diuji โ real
- Dependency โ mock
2. Real vs Spyโ
- Semua method aman โ real
- Perlu kontrol sebagian โ spyk
3. Mock vs Fakeโ
- Perlu verify / response โ mock
- Perlu behavior sederhana stabil โ fake
4. Assert vs Verifyโ
- Fokus hasil โ assert
- Fokus interaksi โ verify
5. Coroutine Testโ
- Ada coroutine โ runTest
- Tidak โ tidak perlu
6. Flowโ
- Simple โ
first() - Complex โ Turbine
7. LiveDataโ
- Pakai LiveData โ InstantTaskExecutorRule
- Tidak โ skip
8. Stubโ
- Stub seperlunya
- Jangan over-stub
9. Test Structureโ
Gunakan AAA:
// Arrange
// Act
// Assert
10. Test Namingโ
Gunakan format:
fun `methodName, expected behavior when condition`()
Contoh:
fun `getCached, returns null when cache is empty`()
๐ง Best Practiceโ
1. Test Behavior, bukan Implementationโ
Test:
- input
- output
- side effect
Bukan:
- variable internal
- function private
2. Satu Test = Satu Scenarioโ
โ Buruk:
- 1 test banyak scenario
โ Bagus:
- 1 scenario = 1 test
3. Coverage bukan segalanyaโ
Coverage tinggi โ test bagus
Yang penting:
- assertion kuat
- scenario lengkap
4. Repetition di test itu OKโ
Test readable > test DRY
5. Edge Case pentingโ
Selalu test:
- null
- empty
- boundary
- true/false
6. Verify secukupnyaโ
Gunakan verify hanya jika:
- interaksi penting
Jangan semua diverify โ test jadi rapuh
7. Kalau test susah โ cek designโ
Tanda code bermasalah:
- terlalu banyak mock
- butuh banyak spyk
- setup panjang
Solusi:
- pecah class
- inject dependency
- pisahkan logic
8. Tools sesuai kebutuhanโ
- MockK โ mocking
- runTest โ coroutine
- Turbine โ Flow
- Truth โ assertion
- InstantTaskExecutorRule โ LiveData
9. Test membantu detect bugโ
Unit test membantu menemukan:
- bug logic
- dependency problem
- code smell
๐ Summary (Super Cheat Sheet)โ
| Kondisi | Gunakan |
|---|---|
| object diuji | โ REAL |
| dependency | โ MOCK |
| partial control | โ SPYK |
| Flow simple | โ first() |
| Flow complex | โ Turbine |
| coroutine | โ runTest |
| fokus hasil | โ assert |
| fokus interaksi | โ verify |
๐ฅ 5 Pegangan Utamaโ
- Test behavior, bukan internal detail
- Class diuji real, dependency mock
- Gunakan AAA & naming jelas
- Coverage penting, tapi bukan tujuan
- Kalau test susah โ kemungkinan design code bermasalah