android-sdd 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,288 @@
1
+ ---
2
+ name: integration-testing
3
+ description: >
4
+ Integration testing for Android — testing multiple components together.
5
+ Load this skill when testing Repository with a real Room database,
6
+ testing ViewModel with real UseCases, verifying data flow across layers,
7
+ or using Hilt for test dependency injection.
8
+ ---
9
+
10
+ # Integration Testing
11
+
12
+ ## Overview
13
+ Integration tests verify that multiple components work correctly together. Common targets: Repository + Room (real DB), ViewModel + UseCase + fake Repository, or full data layer with in-memory database. These run on a device or emulator (androidTest) or on JVM with Robolectric.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Integration tests sit **between unit and UI tests** — test real wiring, fake I/O
20
+ - Use **in-memory Room databases** — fast, no disk I/O, auto-cleaned
21
+ - Use **Hilt testing** — swap real implementations with fakes via `@TestInstallIn`
22
+ - Run on **JVM with Robolectric** where possible — faster than device
23
+ - Keep integration tests **focused** — one subsystem per test class
24
+
25
+ ---
26
+
27
+ ## Dependencies
28
+
29
+ ```kotlin
30
+ // build.gradle.kts
31
+ dependencies {
32
+ // Instrumented tests (androidTest)
33
+ androidTestImplementation(libs.hilt.android.testing)
34
+ kspAndroidTest(libs.hilt.compiler)
35
+ androidTestImplementation(libs.androidx.test.runner)
36
+ androidTestImplementation(libs.androidx.test.rules)
37
+ androidTestImplementation(libs.kotlinx.coroutines.test)
38
+ androidTestImplementation(libs.turbine)
39
+
40
+ // JVM tests with Robolectric (test)
41
+ testImplementation(libs.robolectric)
42
+ testImplementation(libs.androidx.test.core)
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Repository + Room Integration Test
49
+
50
+ ```kotlin
51
+ // ✅ Test Repository with real in-memory Room database
52
+ @RunWith(AndroidJUnit4::class)
53
+ class UserRepositoryIntegrationTest {
54
+
55
+ private lateinit var database: AppDatabase
56
+ private lateinit var dao: UserDao
57
+ private lateinit var api: UserApiService
58
+ private lateinit var repository: UserRepositoryImpl
59
+
60
+ @Before
61
+ fun setup() {
62
+ val context = ApplicationProvider.getApplicationContext<Context>()
63
+ database = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
64
+ .allowMainThreadQueries() // ✅ allowed in tests only
65
+ .build()
66
+ dao = database.userDao()
67
+ api = mockk()
68
+ repository = UserRepositoryImpl(dao, api)
69
+ }
70
+
71
+ @After
72
+ fun teardown() {
73
+ database.close()
74
+ }
75
+
76
+ @Test
77
+ fun `getUser caches API response in local database`() = runTest {
78
+ // Arrange
79
+ val dto = UserDto(id = "1", name = "Ali", email = "ali@test.com", role = "member")
80
+ coEvery { api.getUser("1") } returns dto
81
+
82
+ // Act — first call fetches from API
83
+ val result = repository.getUser("1")
84
+
85
+ // Assert
86
+ assertThat(result.isSuccess).isTrue()
87
+ assertThat(result.getOrNull()?.name).isEqualTo("Ali")
88
+
89
+ // Verify cached — second call should not hit API
90
+ repository.getUser("1")
91
+ coVerify(exactly = 1) { api.getUser("1") }
92
+
93
+ // Verify in DB
94
+ val cached = dao.getById("1")
95
+ assertThat(cached).isNotNull()
96
+ assertThat(cached?.name).isEqualTo("Ali")
97
+ }
98
+
99
+ @Test
100
+ fun `observeUsers emits updates when database changes`() = runTest {
101
+ // Arrange
102
+ repository.observeUsers().test {
103
+ assertThat(awaitItem()).isEmpty()
104
+
105
+ // Act — insert directly into DB
106
+ dao.insert(UserEntity(id = "1", name = "Ali", email = "ali@test.com", role = "member"))
107
+
108
+ // Assert — Flow emits new value
109
+ val users = awaitItem()
110
+ assertThat(users).hasSize(1)
111
+ assertThat(users[0].name).isEqualTo("Ali")
112
+
113
+ cancelAndIgnoreRemainingEvents()
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Hilt Integration Test
122
+
123
+ ```kotlin
124
+ // ✅ Replace real dependencies with fakes for specific tests
125
+
126
+ // 1. Define a fake module
127
+ @Module
128
+ @TestInstallIn(
129
+ components = [SingletonComponent::class],
130
+ replaces = [UserRepositoryModule::class] // replaces real binding
131
+ )
132
+ abstract class FakeUserRepositoryModule {
133
+ @Binds
134
+ @Singleton
135
+ abstract fun bindUserRepository(fake: FakeUserRepository): UserRepository
136
+ }
137
+
138
+ // 2. Inject fake into test
139
+ @HiltAndroidTest
140
+ class UserListViewModelIntegrationTest {
141
+
142
+ @get:Rule(order = 0)
143
+ val hiltRule = HiltAndroidRule(this)
144
+
145
+ @get:Rule(order = 1)
146
+ val mainDispatcherRule = MainDispatcherRule()
147
+
148
+ @Inject lateinit var fakeRepository: FakeUserRepository
149
+ @Inject lateinit var getUsersUseCase: GetUsersUseCase
150
+
151
+ private lateinit var viewModel: UserListViewModel
152
+
153
+ @Before
154
+ fun setup() {
155
+ hiltRule.inject()
156
+ viewModel = UserListViewModel(getUsersUseCase)
157
+ }
158
+
159
+ @Test
160
+ fun `ViewModel reflects repository data changes`() = runTest {
161
+ viewModel.state.test {
162
+ assertThat(awaitItem()).isEqualTo(UserListUiState.Loading)
163
+
164
+ val users = listOf(User(id = "1", name = "Ali", email = Email("ali@test.com")))
165
+ fakeRepository.emit(users)
166
+
167
+ val success = awaitItem() as UserListUiState.Success
168
+ assertThat(success.users).isEqualTo(users)
169
+
170
+ cancelAndIgnoreRemainingEvents()
171
+ }
172
+ }
173
+ }
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Robolectric (JVM-based Android Tests)
179
+
180
+ ```kotlin
181
+ // ✅ Run Android-dependent tests on JVM — faster than emulator
182
+ @RunWith(RobolectricTestRunner::class)
183
+ @Config(sdk = [34])
184
+ class UserRepositoryRobolectricTest {
185
+
186
+ private lateinit var database: AppDatabase
187
+ private lateinit var repository: UserRepositoryImpl
188
+
189
+ @Before
190
+ fun setup() {
191
+ val context = ApplicationProvider.getApplicationContext<Context>()
192
+ database = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
193
+ .allowMainThreadQueries()
194
+ .build()
195
+ repository = UserRepositoryImpl(database.userDao(), mockk())
196
+ }
197
+
198
+ @After
199
+ fun teardown() { database.close() }
200
+
201
+ @Test
202
+ fun `delete user removes from database`() = runTest {
203
+ // Arrange
204
+ database.userDao().insert(
205
+ UserEntity(id = "1", name = "Ali", email = "ali@test.com", role = "member")
206
+ )
207
+
208
+ // Act
209
+ repository.deleteUser("1")
210
+
211
+ // Assert
212
+ val result = database.userDao().getById("1")
213
+ assertThat(result).isNull()
214
+ }
215
+ }
216
+ ```
217
+
218
+ ---
219
+
220
+ ## DAO Integration Test
221
+
222
+ ```kotlin
223
+ // ✅ Test DAO queries with real Room in-memory database
224
+ @RunWith(AndroidJUnit4::class)
225
+ class UserDaoTest {
226
+
227
+ private lateinit var database: AppDatabase
228
+ private lateinit var dao: UserDao
229
+
230
+ @Before
231
+ fun setup() {
232
+ val context = ApplicationProvider.getApplicationContext<Context>()
233
+ database = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
234
+ .allowMainThreadQueries()
235
+ .build()
236
+ dao = database.userDao()
237
+ }
238
+
239
+ @After
240
+ fun teardown() { database.close() }
241
+
242
+ @Test
243
+ fun `insert and retrieve user`() = runTest {
244
+ val entity = UserEntity(id = "1", name = "Ali", email = "ali@test.com", role = "member")
245
+ dao.insert(entity)
246
+
247
+ val retrieved = dao.getById("1")
248
+ assertThat(retrieved).isEqualTo(entity)
249
+ }
250
+
251
+ @Test
252
+ fun `observeAll emits empty list initially`() = runTest {
253
+ dao.observeAll().test {
254
+ assertThat(awaitItem()).isEmpty()
255
+ cancelAndIgnoreRemainingEvents()
256
+ }
257
+ }
258
+
259
+ @Test
260
+ fun `delete removes entity`() = runTest {
261
+ val entity = UserEntity(id = "1", name = "Ali", email = "ali@test.com", role = "member")
262
+ dao.insert(entity)
263
+ dao.deleteById("1")
264
+
265
+ assertThat(dao.getById("1")).isNull()
266
+ }
267
+ }
268
+ ```
269
+
270
+ ---
271
+
272
+ ## Anti-Patterns
273
+
274
+ - Using real network in integration tests — mock the API layer; only test DB + logic integration
275
+ - Not closing the in-memory database in `@After` — causes resource leaks between tests
276
+ - `allowMainThreadQueries()` in production code — only acceptable in tests
277
+ - Testing too many layers at once — keep each integration test focused on one boundary
278
+ - Slow tests due to full Hilt component initialization — use `@UninstallModules` selectively
279
+
280
+ ---
281
+
282
+ ## Related Skills
283
+ - `unit-testing` — isolated unit tests for individual classes
284
+ - `ui-testing` — Compose UI tests on device/emulator
285
+ - `mocking` — MockK patterns used in integration tests
286
+ - `fake-data` — test data factories
287
+ - `hilt` — Hilt test setup with `@HiltAndroidTest`
288
+ - `room` — Room database under test
@@ -0,0 +1,229 @@
1
+ ---
2
+ name: mocking
3
+ description: >
4
+ Mocking with MockK for Android unit tests.
5
+ Load this skill when creating mocks, stubs, and spies with MockK,
6
+ stubbing suspend functions and Flows, verifying interactions,
7
+ using argument captors, or deciding between mocks and fakes.
8
+ ---
9
+
10
+ # Mocking
11
+
12
+ ## Overview
13
+ MockK is the idiomatic mocking library for Kotlin. It supports suspend functions, coroutines, extension functions, and object mocking — areas where Mockito struggles with Kotlin code. Use mocks to isolate the class under test from its dependencies and verify interactions.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **Fakes over mocks** for repositories and data sources — fakes are more maintainable
20
+ - **Mocks** for verifying interactions — "was this method called with these arguments?"
21
+ - **Stubs** for providing return values — "when called, return this"
22
+ - **Never mock what you don't own** — mock your own interfaces, not third-party classes
23
+ - `coEvery` / `coVerify` for suspend functions — never use `every`/`verify` for coroutines
24
+
25
+ ---
26
+
27
+ ## Creating Mocks
28
+
29
+ ```kotlin
30
+ // ✅ Create mock
31
+ val repository = mockk<UserRepository>()
32
+
33
+ // ✅ Relaxed mock — returns default values without explicit stubs
34
+ val repository = mockk<UserRepository>(relaxed = true)
35
+
36
+ // ✅ Relaxed Unit mock — only relaxes Unit-returning functions
37
+ val repository = mockk<UserRepository>(relaxUnitFun = true)
38
+
39
+ // ✅ Spy — wraps real object, overrides only stubbed methods
40
+ val realRepository = UserRepositoryImpl(dao, api)
41
+ val spy = spyk(realRepository)
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Stubbing
47
+
48
+ ```kotlin
49
+ // ✅ Stub regular function
50
+ every { repository.getCount() } returns 5
51
+ every { repository.getCount() } returns 5 andThen 10 // different on subsequent calls
52
+ every { repository.getCount() } throws RuntimeException("error")
53
+
54
+ // ✅ Stub suspend function
55
+ coEvery { repository.getUser("1") } returns Result.success(user)
56
+ coEvery { repository.getUser("1") } throws IOException("Network error")
57
+ coEvery { repository.getUser(any()) } returns Result.failure(Exception("Not found"))
58
+
59
+ // ✅ Stub Flow
60
+ every { repository.observeUsers() } returns flowOf(listOf(user))
61
+ every { repository.observeUsers() } returns flow {
62
+ emit(emptyList())
63
+ delay(100)
64
+ emit(listOf(user))
65
+ }
66
+
67
+ // ✅ Stub with argument matching
68
+ coEvery { repository.getUser(match { it.startsWith("user_") }) } returns Result.success(user)
69
+ coEvery { repository.getUser(any()) } returns Result.failure(Exception())
70
+
71
+ // ✅ Stub with answer (access arguments)
72
+ coEvery { repository.getUser(any()) } answers {
73
+ val id = firstArg<String>()
74
+ Result.success(User(id = id, name = "User $id", email = Email("$id@test.com")))
75
+ }
76
+
77
+ // ✅ Unit-returning suspend — use coJustRun
78
+ coJustRun { repository.deleteUser(any()) }
79
+
80
+ // ✅ Non-suspend Unit — use justRun
81
+ justRun { analytics.log(any()) }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Verification
87
+
88
+ ```kotlin
89
+ // ✅ Verify call was made
90
+ verify { repository.getCount() }
91
+ coVerify { repository.getUser("1") }
92
+
93
+ // ✅ Verify call count
94
+ verify(exactly = 1) { repository.getCount() }
95
+ verify(exactly = 0) { repository.getCount() } // never called
96
+ verify(atLeast = 1) { repository.getCount() }
97
+ verify(atMost = 2) { repository.getCount() }
98
+
99
+ // ✅ Verify with argument matchers
100
+ coVerify { repository.getUser(eq("1")) }
101
+ coVerify { repository.getUser(any()) }
102
+ coVerify { repository.getUser(match { it.isNotEmpty() }) }
103
+
104
+ // ✅ Verify order
105
+ verifyOrder {
106
+ repository.getCount()
107
+ repository.getUser("1")
108
+ }
109
+
110
+ // ✅ Verify sequence (exactly in order, no other calls)
111
+ verifySequence {
112
+ repository.getCount()
113
+ repository.getUser("1")
114
+ }
115
+
116
+ // ✅ Verify no interactions
117
+ verify { repository wasNot Called }
118
+ confirmVerified(repository) // all interactions accounted for
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Argument Captors
124
+
125
+ ```kotlin
126
+ // ✅ Capture arguments for complex assertions
127
+ val userSlot = slot<User>()
128
+ coEvery { repository.createUser(capture(userSlot)) } returns Result.success(user)
129
+
130
+ viewModel.onCreateUser("Ali", "ali@test.com")
131
+
132
+ assertThat(userSlot.captured.name).isEqualTo("Ali")
133
+ assertThat(userSlot.captured.email.value).isEqualTo("ali@test.com")
134
+
135
+ // ✅ Capture multiple calls
136
+ val capturedIds = mutableListOf<String>()
137
+ coEvery { repository.getUser(captureMany = capturedIds) } returns Result.success(user)
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Mocking Objects and Companions
143
+
144
+ ```kotlin
145
+ // ✅ Mock Kotlin object
146
+ mockkObject(DateUtils)
147
+ every { DateUtils.now() } returns Instant.parse("2024-01-01T00:00:00Z")
148
+ // ... test ...
149
+ unmockkObject(DateUtils)
150
+
151
+ // ✅ Mock companion object
152
+ mockkObject(User.Companion)
153
+ every { User.Companion.create(any()) } returns mockUser
154
+
155
+ // ✅ Mock top-level functions
156
+ mockkStatic("com.example.app.utils.ExtensionsKt")
157
+ every { anyString().toFormattedDate() } returns "Jan 1, 2024"
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Clearing Mocks
163
+
164
+ ```kotlin
165
+ // ✅ Clear recorded calls (keep stubs)
166
+ clearMocks(repository, answers = false)
167
+
168
+ // ✅ Clear stubs and calls
169
+ clearMocks(repository)
170
+
171
+ // ✅ Clear all mocks in a test class
172
+ @AfterEach
173
+ fun teardown() {
174
+ clearAllMocks()
175
+ }
176
+
177
+ // ✅ Unmock objects
178
+ @AfterEach
179
+ fun teardown() {
180
+ unmockkAll()
181
+ }
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Mocks vs Fakes Decision Guide
187
+
188
+ | Scenario | Use |
189
+ |---|---|
190
+ | Repository with simple CRUD | Fake |
191
+ | Repository — verify specific API calls | Mock |
192
+ | External service (Analytics, Firebase) | Mock |
193
+ | Database layer in integration test | Real (in-memory Room) |
194
+ | Verifying a method was called N times | Mock |
195
+ | Controlling returned data in multiple tests | Fake |
196
+
197
+ ```kotlin
198
+ // ✅ Fake — simpler, no verify needed
199
+ class FakeUserRepository : UserRepository {
200
+ private val _users = MutableStateFlow<List<User>>(emptyList())
201
+ var shouldFail = false
202
+
203
+ override fun observeUsers() = _users.asStateFlow()
204
+ override suspend fun getUser(id: String) =
205
+ if (shouldFail) Result.failure(Exception("error"))
206
+ else _users.value.find { it.id == id }
207
+ ?.let { Result.success(it) }
208
+ ?: Result.failure(Exception("not found"))
209
+
210
+ fun emit(users: List<User>) { _users.value = users }
211
+ }
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Anti-Patterns
217
+
218
+ - `every` for suspend functions — use `coEvery`; `every` will not work correctly
219
+ - `verify` for suspend functions — use `coVerify`
220
+ - Mocking data classes — just create them directly
221
+ - Over-verifying — only verify interactions that matter for the behavior being tested
222
+ - Not clearing mocks between tests — stale stubs cause test pollution
223
+
224
+ ---
225
+
226
+ ## Related Skills
227
+ - `unit-testing` — full unit test structure using MockK
228
+ - `fake-data` — building fake implementations and test data
229
+ - `integration-testing` — when to use fakes vs mocks