This commit is contained in:
coco
2026-07-15 16:57:36 +08:00
parent 723ce1af5c
commit 95aa018b8b
314 changed files with 8910 additions and 0 deletions
@@ -0,0 +1,134 @@
# UI Testing
## Setup
Add the Compose testing library to your dependencies in build.gradle
```kotlin
androidTestImplementation("androidx.compose.ui:ui-test:$compose_version")
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
```
## Example
Let's say you have the following example code, it's a button with a text that says **Hello** and when you click on it, it turns to **Bye**
```kotlin
@Composable
fun TestingExample() {
val state = remember { mutableStateOf("Hello") }
Button(onClick = { state.value = "Bye" }) {
Text(state.value)
}
}
```
## TestRule
To run this Composable in your test Code you have 2 options:
### 1) createComposeRule()
```kotlin
@Rule
@JvmField
var composeTestRule: ComposeContentTestRule = createComposeRule()
```
You can use this when you want to run your Composable without a specific Activity. You can then use the **setContent()** from the TestRule to host your Composable.
```kotlin
composeTestRule.setContent {
TestingExample()
}
```
### 2) createAndroidComposeRule()
```kotlin
@Rule
@JvmField
var composeTestRule: ComposeContentTestRule = createAndroidComposeRule<UiTestingDemoActivity>()
```
You can use this when you want to start your test with a specific Activity.
## Interaction with Composables
Now we want to test how the Composable reacts when we interact with it.
Usually in Tests we would use Espresso for that, but you can use that only for classic Android Views and not for Composables.
The ComposeTestRule will offer a similar API
```kotlin
class ExampleUiTestWithAndroidComposeRule {
@Rule
@JvmField
var composeTestRule: ComposeContentTestRule = createAndroidComposeRule<UiTestingDemoActivity>()
@Test
fun whenIClickOnButton_TheTextShouldChange() {
composeTestRule.onNodeWithText("Hello").assertExists()
composeTestRule.onNodeWithText("Hello").performClick()
composeTestRule.onNodeWithText("Hello").assertDoesNotExist()
composeTestRule.onNodeWithText("Bye").assertExists()
}
}
```
As you can see above we used the TestRule methods to click on the node with the text **Hello** and asserted that the text changed to **Bye**
The TestRule offers a lot of different methods. You can find a cheatSheet [here](https://developer.android.com/jetpack/compose/testing-cheatsheet)
### TestTags
The test above is only half correct, because it looks for the text inside the button and not the button itself.
Composables have no resource ids so we cant just use **onView(withId(R.id.my_view))** to find a Composable in a test, also we can't find Composables of a specific "type"
like a button, because everything is just a Composable function.
When you can't find a Composable by a text and you want to make it detectable in your test. Jetpack Compose offers the concept of a **TestTag**.
testTag is a modifier that needs to be set to a Composable. It expects a string which will be used as a reference in your test.
```kotlin
@Composable
fun TestingExample() {
val state = remember { mutableStateOf("Hello") }
Button(onClick = { state.value = "Bye" }, modifier = Modifier.testTag("MyTestTag")) {
Text(state.value)
}
}
```
Now you can use **onNodeWithTag("MyTestTag")** to find the button Composable.
```kotlin
@Test
fun whenIClickOnButton_TheTextShouldChange() {
composeTestRule.onNodeWithTag("MyTestTag").assertTextEquals("Hello")
composeTestRule.onNodeWithTag("MyTestTag").performClick()
composeTestRule.onNodeWithText("Hello").assertDoesNotExist()
composeTestRule.onNodeWithTag("MyTestTag").assertTextEquals("Bye")
}
```
### PrintToLog
When you want to get more information about how the node tree of a Composable looks like,
you can use **printToLog()** on a node.
```kotlin
composeTestRule.onNodeWithTag("MyTestTag").printToLog("XXX")
```
will print to logcat:
```kotlin
2022-01-07 22:58:55.048 9567-9587/de.jensklingenberg.jetpackcomposeplayground D/XXX: printToLog:
Printing with useUnmergedTree = 'false'
Node #2 at (l=0.0, t=325.0, r=195.0, b=424.0)px, Tag: 'MyTestTag'
Role = 'Button'
Text = '[Hello]'
Actions = [OnClick, GetTextLayoutResult]
MergeDescendants = 'true'
```
## See also:
* [Full Example Code](https://github.com/Foso/Jetpack-Compose-Playground/blob/master/app/src/androidTest/java/de/jensklingenberg/jetpackcomposeplayground/ExampleUiTestWithAndroidComposeRule.kt)
* [Full Example Code](https://github.com/Foso/Jetpack-Compose-Playground/blob/master/app/src/androidTest/java/de/jensklingenberg/jetpackcomposeplayground/ExampleUiTestWithComposeRule.kt)