String resources provide multiple benefits, such as localization and accessibility support, dynamic string formatting and maintenance of the application.
When we create UI test cases, we often want to reuse strings from the app to avoid issues when a test device has different locale than the default language of the application. Unfortunately, the "UiAutomator" doesn't allow us to get strings using the generated R
class.
There are a few popular cases of using the UiAutomator framework:
We cannot verify both cases with the Espresso based framework because notifications and permission dialog are part of the OS (Operating System), not part of the application. However, when we test notifications, we use string values from resources and we want to reuse string resources to avoid failure when we update the string resource value.
Let's take a look at part of the test cases that interact with a notification:
val expectedText = TestData.getCityById(amsterdamId).description
...
uiDevice.openNotification()
uiDevice.wait(Until.hasObject(By.text("DemoApp")), TIMEOUT)
uiDevice.findObject(By.text(expectedText))
.click()
...
The major problem in this example is the hard-coded string. Let's explore how we can use string resources with the UiAutomator framework.
The first option is to use the By.res(PACKAGE, RESOURCE_ID)
function. Here, we need to pass package and resource ID as strings and we can easily make a typo.
Let's explore how to get a value from a string resources by using the R
class, like R.string.app_name
. To get a value from the string resource, we need to have application context, which we can get from the InstrumentationRegistry
.
InstrumentationRegistry.getInstrumentation().targetContext
The next step is to create a function that returns a string value from resource id.
private fun stringResource(
@StringRes resId: Int,
context: Context = InstrumentationRegistry.getInstrumentation().targetContext
): String {
return context.getString(resId)
}
Now, we can use string resources in our test case:
val expectedText = TestData.getCityById(amsterdamId).description
...
uiDevice.openNotification()
uiDevice.wait(Until.hasObject(By.text(stringResource(R.string.app_name))), TIMEOUT)
uiDevice.findObject(By.textStartsWith(expectedText))
.click()
...
When we use string resources (R.string.*
) in our test case, we have a single point of truth for our strings. In addition to that, we will have a compilation error if we will update the name of the string resource. It helps us to find and fix an error faster.