@@ -177,20 +177,44 @@ Qualified<Executor> bgExecutor = qualified(Background.class, Executor.class);
177
177
Executor sequentialExecutor = FirebaseExecutors . newSequentialExecutor(c. get(bgExecutor));
178
178
```
179
179
180
+ #### Proper Kotlin usage
181
+
182
+ A ` CoroutineContext ` should be preferred when possible over an explicit ` Executor `
183
+ or ` CoroutineDispatcher ` . You should only use your ` Executor ` at the highest
184
+ (or inversely the lowest) level of your implementations. Most classes should not
185
+ be concerned with the existence of an ` Executor ` .
186
+
187
+ Keep in mind that you can combine ` CoroutineContext ` with other ` CoroutineScope `
188
+ or ` CoroutineContext ` . And that all ` suspend ` functions inherent their ` coroutineContext ` :
189
+
190
+ ``` kotlin
191
+ suspend fun createSession (): Session {
192
+ val context = backgroundDispatcher.coroutineContext + coroutineContext
193
+ return Session (context)
194
+ }
195
+ ```
196
+
197
+ To learn more, you should give the following Kotlin wiki page a read:
198
+
199
+ [ Coroutine context and dispatchers] ( https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html#dispatchers-and-threads )
200
+
180
201
## Testing
181
202
203
+ ### Using Executors in tests
204
+
182
205
` @Lightweight ` and ` @Background ` executors have StrictMode enabled and throw exceptions on violations.
183
206
For example trying to do Network IO on either of them will throw.
184
207
With that in mind, when it comes to writing tests, prefer to use the common executors as opposed to creating
185
208
your own thread pools. This will ensure that your code uses the appropriate executor and does not slow down
186
209
all of Firebase by using the wrong one.
187
210
188
- To do that, you should prefer relying on Components to inject the right executor even in tests. This will ensure
189
- your tests are always using the executor that is actually used in your SDK build.
190
- If your SDK uses Dagger, see [ Dependency Injection] ({{ site.baseurl }}{% link best_practices/dependency_injection.md %})
211
+ To do that, you should prefer relying on Components to inject the right executor even in tests.
212
+ This will ensure your tests are always using the executor that is actually used in your SDK build.
213
+ If your SDK uses Dagger, see [ Dependency Injection] ({{ site.baseurl }}{% link
214
+ best_practices/dependency_injection.md %})
191
215
and [ Dagger's testing guide] ( https://dagger.dev/dev-guide/testing ) .
192
216
193
- When the above is not an option, you can use ` TestOnlyExecutors ` , but make sure you're testing your code with
217
+ When the above is not an option, you can use ` TestOnlyExecutors ` , but make sure you're testing your code with
194
218
the same executor that is used in production code:
195
219
196
220
``` kotlin
@@ -200,7 +224,6 @@ dependencies {
200
224
// or
201
225
androidTestImplementation(project(" :integ-testing" ))
202
226
}
203
-
204
227
```
205
228
206
229
This gives access to
@@ -211,3 +234,53 @@ TestOnlyExecutors.background();
211
234
TestOnlyExecutors . blocking();
212
235
TestOnlyExecutors . lite();
213
236
```
237
+
238
+ ### Policy violations in tests
239
+
240
+ Unit tests require [ Robolectric] ( https://github.com/robolectric/robolectric ) to
241
+ function correctly, and this comes with a major drawback; no policy validation.
242
+
243
+ Robolectric supports ` StrictMode ` - but does not provided the backing for its
244
+ policy mechanisms to fire on violations. As such, you'll be able to do things
245
+ like using ` TestOnlyExecutors.background() ` to execute blocking actions; usage
246
+ that would've otherwise crashed in a real application.
247
+
248
+ Unfortunately, there is no easy way to fix this for unit tests. You can get
249
+ around the issue by moving the tests to an emulator (integration tests)- but
250
+ those can be more expensive than your standard unit tests, so you may want to
251
+ take that into consideration when planning your testing strategy.
252
+
253
+ ### StandardTestDispatcher support
254
+
255
+ The [ kotlin.coroutines.test] ( https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/ )
256
+ library provides support for a number of different mechanisms in tests. Some of the more
257
+ famous features include:
258
+
259
+ - [ advanceUntilIdle] ( https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/advance-until-idle.html )
260
+ - [ advanceTimeBy] ( https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/advance-time-by.html )
261
+ - [ runCurrent] ( https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/run-current.html )
262
+
263
+ These features are all backed by ` StandardTestDispatcher ` , or more appropriately,
264
+ the ` TestScope ` provided in a ` runTest ` block.
265
+
266
+ Unfortunately, ` TestOnlyExecutors ` does not natively bind with ` TestScope ` .
267
+ Meaning, should you use ` TestOnlyExecutors ` in your tests- you won't be able to utilize
268
+ the features provided by ` TestScope ` .
269
+
270
+ To help fix this, we provide an extension method on ` TestScope ` called
271
+ ` firebaseLibrary ` . It facilitates the binding of ` TestOnlyExecutors ` with the
272
+ current ` TestScope ` .
273
+
274
+ For example, here's how you could use this extension method in a test:
275
+
276
+ ``` kotlin
277
+ @Test
278
+ fun doesStuff () = runTest {
279
+ val scope = CoroutineScope (firebaseExecutors.background)
280
+ scope.launch {
281
+ // ... does stuff
282
+ }
283
+
284
+ runCurrent()
285
+ }
286
+ ```
0 commit comments