android-test icon indicating copy to clipboard operation
android-test copied to clipboard

Provide a Rule for overriding app locale in instrumented tests

Open angusholder opened this issue 3 years ago • 1 comments

Please provide a standard way to override the app's Locale in tests. Main use cases for me are testing against a given language translation, and for tests on screens that display currency amount where the format depends on your locale, eg:

  • en_US displays GBP2.00
  • en_GB displays £2.00

so I need to set a locale to make sure my UI assertions match the locale-dependent text.

I currently use

class LocaleOverrideRule(
    private val getLocale: () -> Locale,
) : ExternalResource() {
    private lateinit var previousLocale: Locale
    override fun before() {
        previousLocale = Locale.getDefault()
        val newLocale = getLocale()

        Locale.setDefault(newLocale)
        // We also have to update Locale in the Configuration, otherwise Locale.setDefault() gets
        // overwritten with Configuration.locale's value at startup.
        val resources: Resources = InstrumentationRegistry.getInstrumentation().targetContext.resources
        val config: Configuration = resources.configuration
        config.setLocale(newLocale)
        resources.updateConfiguration(config, resources.displayMetrics)

        println("Setting locale from $previousLocale to ${Locale.getDefault()}")
        println("Format locale is now ${Locale.getDefault(Locale.Category.FORMAT)}")
    }

    override fun after() {
        println("Restoring locale from ${Locale.getDefault()} to $previousLocale")
        Locale.setDefault(previousLocale)
    }
}

which works, but it relies on the deprecated Resources.updateConfiguration(). It recommends replacing that with Context.createConfigurationContext(Configuration) but I don't see an obvious way to provide a context that the ActivityScenario should use. The upcoming AppCompatDelegate.setAppLocales() may work for this purpose.

Either way, I feel like this is stuff is hard enough to work out that AndroidX test should provide a built-in solution.

angusholder avatar Jun 20 '22 15:06 angusholder

Same problem gere, but i've fond another solution. AppCompatDelegate.setAppLocales() and LocalManager not working (recreate activity while testing, not good 😄 )

So i use a Restricted API

@SuppressLint("RestrictedApi")
    @Before
    fun configureLanguage() {
        val locales = LocaleListCompat.forLanguageTags("fr").toLanguageTags()
        AppLocalesStorageHelper.persistLocales(context, locales)
    }

PS: Why ? Because with per app language implementation, your solution don't work for me.

PS2: unfortunately, this method create a double launch of my activity under testing ...

jmartinMone avatar Feb 27 '25 09:02 jmartinMone