In Jetpack compose I have a TextField and I'm trying to write Espresso UI tests.
I didn't find how I can enter text in the TextField, any ideas, please?
TextField(
value = textState.value,
modifier = Modifier.fillMaxWidth(),
onValueChange = {
textState.value = it
apiServiceCall(textState.value.text)
},
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences)
),
)
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
@Test
fun enterTextAndMakeServiceCall() {
ActivityScenario.launch(MainActivity::class.java)
// TODO: Enter text inside the TextField
composeTestRule.onNode(hasText(getString(R.string.result)))
}
To read value entered in TextField in Android Compose, declare a variable, and assign this variable the value in the TextField whenever there is a change in the value. The same process holds for reading value entered in OutlineTextField composable.
Creating your first Jetpack Compose project is surprisingly easy. Just download the latest Canary version of Android studio and select a new Compose project from the list of possible Android projects. To start you'll have a simple “Hello world!” Text composable.
mutableStateOf creates an observable MutableState<T> , which is an observable type integrated with the compose runtime. interface MutableState<T> : State<T> { override var value: T. } Any changes to value will schedule recomposition of any composable functions that read value .
A LazyColumn is a vertically scrolling list that only composes and lays out the currently visible items. It's similar to a Recyclerview in the classic Android View system.
I first set the testTag
modifier on the composable I want to test:
const val MY_TEXTFIELD_TAG = "myTextFieldTag"
TextField(
value = textState.value,
modifier = Modifier.fillMaxWidth().testTag(MY_TEXTFIELD_TAG),
onValueChange = {
textState.value = it
},
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences),
)
And then from your test you can set and check the value like this:
@Test
fun setAndCheckTheTextFieldValue() {
ActivityScenario.launch(MainActivity::class.java)
val resultText = "result"
// Sets the TextField value
composeTestRule.onNodeWithTag(MY_TEXTFIELD_TAG).performTextInput(resultText)
// Asserts the TextField has the corresponding value
composeTestRule.onNodeWithTag(MY_TEXTFIELD_TAG).assert(hasText(resultText))
}
UPDATE:
Another way I use lately is to use the contentDescription
instead.
Let's say you have a TextField with content description like this one (not using state hoisting for simplicity on this sample):
@Composable
fun MyTextField() {
val textState = remember { mutableStateOf(TextFieldValue()) }
val textFieldContentDescription = stringResource(id = R.string.text_field_content_description)
TextField(
value = textState.value,
modifier = Modifier
.fillMaxWidth()
.semantics { contentDescription = textFieldContentDescription },
onValueChange = {
textState.value = it
},
)
}
The test could be something like:
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun setAndCheckTheTextFieldValue() {
lateinit var textFieldContentDescription: String
composeTestRule.setContent {
textFieldContentDescription = stringResource(id = R.string.text_field_content_description)
MaterialTheme {
MyTextField()
}
}
val resultText = "result"
// Sets the TextField value
composeTestRule.onNodeWithContentDescription(textFieldContentDescription).performTextInput(resultText)
// Asserts the TextField has the corresponding value
composeTestRule.onNodeWithContentDescription(textFieldContentDescription).assert(hasText(resultText, ignoreCase = true))
}
and this way the app is more accessible as well by having content descriptions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With