Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enter text in Jetpack compose TextField through UI tests?

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)))
}
like image 231
AndroidDev Avatar asked Mar 17 '21 14:03

AndroidDev


People also ask

How do I get TextField value in jetpack compose?

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.

Is jetpack compose difficult?

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.

What is mutableStateOf in jetpack compose?

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 .

What is LazyColumn in jetpack compose?

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.


1 Answers

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.

like image 197
jeprubio Avatar answered Sep 18 '22 00:09

jeprubio