|
1 |
| -This is a Kotlin Multiplatform project targeting Android, iOS, Web, Desktop. |
| 1 | +## Testing a Kotlin Multiplatform App with Compose and Robolectric |
2 | 2 |
|
3 |
| -* `/composeApp` is for code that will be shared across your Compose Multiplatform applications. |
4 |
| - It contains several subfolders: |
5 |
| - - `commonMain` is for code that’s common for all targets. |
6 |
| - - Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. |
7 |
| - For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, |
8 |
| - `iosMain` would be the right folder for such calls. |
| 3 | +This guide explains how to set up unit tests for a Kotlin Multiplatform application using Compose and Robolectric. |
9 | 4 |
|
10 |
| -* `/iosApp` contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, |
11 |
| - you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project. |
| 5 | +**Why this is important:** |
12 | 6 |
|
| 7 | +* **Shared code:** Kotlin Multiplatform lets you share code between Android, iOS, Desktop, and Web. |
| 8 | +* **Efficient testing:** Robolectric enables fast unit tests by simulating the Android environment on your development machine. |
13 | 9 |
|
14 |
| -Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html), |
15 |
| -[Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/#compose-multiplatform), |
16 |
| -[Kotlin/Wasm](https://kotl.in/wasm/)… |
| 10 | +**What you'll learn:** |
17 | 11 |
|
18 |
| -We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack channel [#compose-web](https://slack-chats.kotlinlang.org/c/compose-web). |
19 |
| -If you face any issues, please report them on [GitHub](https://github.com/JetBrains/compose-multiplatform/issues). |
| 12 | +* How to configure a Kotlin Multiplatform project for testing. |
| 13 | +* How to write instrumented tests that run on a real Android device or emulator. |
| 14 | +* How to write unit tests using Robolectric and JUnit 5. |
| 15 | +* How to increase code coverage with Kover. |
20 | 16 |
|
21 |
| -You can open the web application by running the `:composeApp:wasmJsBrowserDevelopmentRun` Gradle task. |
| 17 | +**Steps:** |
22 | 18 |
|
| 19 | +1. **Project Setup:** |
23 | 20 |
|
24 |
| -https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-test.html |
| 21 | + * Create a `commonTest` directory in your `composeApp` module. |
| 22 | + * Add the following dependencies to your `build.gradle.kts`: |
25 | 23 |
|
26 |
| -https://github.com/apter-tech/junit5-robolectric-extension |
| 24 | + ```kotlin |
| 25 | + kotlin { |
| 26 | + sourceSets { |
| 27 | + commonTest.dependencies { |
| 28 | + implementation(kotlin("test")) |
| 29 | + implementation(compose.uiTest) |
| 30 | + } |
| 31 | + androidInstrumentedTest.dependencies { |
| 32 | + implementation(compose.uiTest) |
| 33 | + } |
| 34 | + } |
| 35 | + androidTarget { |
| 36 | + instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) |
| 37 | + dependencies { |
| 38 | + debugImplementation(libs.androidx.ui.test.manifest) |
| 39 | + } |
| 40 | + } |
| 41 | + } |
| 42 | + android { |
| 43 | + defaultConfig { |
| 44 | + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" |
| 45 | + } |
| 46 | + } |
| 47 | + ``` |
| 48 | + |
| 49 | +2. **Write a Simple Test:** |
| 50 | + |
| 51 | + * Create a test file (e.g., `AppTest.kt`) in `commonTest`. |
| 52 | + * Write a test using `runComposeUiTest` to interact with a Compose component: |
| 53 | + |
| 54 | + ```kotlin |
| 55 | + class AppTest: UsingContext() { |
| 56 | + @Test |
| 57 | + fun myTest() = runComposeUiTest { |
| 58 | + setContent { |
| 59 | + MaterialTheme { |
| 60 | + App() |
| 61 | + } |
| 62 | + } |
| 63 | + onNodeWithText("Click me!").performClick() |
| 64 | + } |
| 65 | + } |
| 66 | + ``` |
| 67 | + |
| 68 | +3. **Run Instrumented Test:** |
| 69 | + |
| 70 | + * Launch an Android emulator. |
| 71 | + * Run the test: `./gradlew composeApp:connectedAndroidTest` |
| 72 | + |
| 73 | +4. **Add Robolectric and JUnit 5:** |
| 74 | + |
| 75 | + * Upgrade to Kotlin 2.1.0. |
| 76 | + * Add the Robolectric plugin and dependencies. |
| 77 | + * Apply the plugin in your `build.gradle.kts`. |
| 78 | + |
| 79 | +5. **Create and Implement `UsingContext`:** |
| 80 | + |
| 81 | + * Create an `expect` class named `UsingContext` in `commonTest`. |
| 82 | + * Implement the `actual` class for Android in `androidTest`, configuring Robolectric. |
| 83 | + * Implement empty `actual` classes for other platforms (iOS, Desktop, Web). |
| 84 | + |
| 85 | +6. **Add an Activity to the Manifest:** |
| 86 | + |
| 87 | + * Add an empty `ComponentActivity` to your `AndroidManifest.xml`. |
| 88 | + |
| 89 | +7. **Run Robolectric Tests:** |
| 90 | + |
| 91 | + * Run: `./gradlew composeApp:testDebugUnitTest` |
| 92 | + |
| 93 | +8. **Exclude `UsingContext` from `check` Task:** |
| 94 | + |
| 95 | + * Prevent JUnit5 from misinterpreting `UsingContext` as a test class: |
| 96 | + |
| 97 | + ```kotlin |
| 98 | + tasks.withType<Test>().configureEach { |
| 99 | + exclude("fr/pitdev/article/kmtest/utils/UsingContext.class") |
| 100 | + } |
| 101 | + ``` |
| 102 | + |
| 103 | + * Run the check task: `./gradlew check` |
| 104 | + |
| 105 | +**Bonus: Code Coverage with Kover** |
| 106 | + |
| 107 | +1. Add Kover dependencies and plugin. |
| 108 | +2. Apply the plugin and configure Kover in your `build.gradle.kts`. |
| 109 | +3. Generate an HTML report: `gradlew koverHtmlReport` |
| 110 | + |
| 111 | +**Learn More:** |
| 112 | + |
| 113 | +* [Kover Repository](https://www.google.com/url?sa=E&source=gmail&q=https://github.com/Kotlin/kotlinx-kover) |
| 114 | +* Example Project [https://github.com/fpitpit/article-km-junit5-robolectric] |
0 commit comments