Skip to main content

๐Ÿงช 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โ€‹

TypeBehavior
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โ€‹

CaseGunakan
Flow 1 emissionfirst()
Flow banyak emissionTurbine

๐Ÿ”น 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)โ€‹

KondisiGunakan
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โ€‹

  1. Test behavior, bukan internal detail
  2. Class diuji real, dependency mock
  3. Gunakan AAA & naming jelas
  4. Coverage penting, tapi bukan tujuan
  5. Kalau test susah โ†’ kemungkinan design code bermasalah