Locale during unit test on Android
Asked Answered
N

3

34

I have some code I want to test. I want to check if a String is properly composed out of various strings that I have in resources. The challenge here is to deal with multiple translations in my resources. I know that locale can be an issue when testing a desktop application and that it is recommended that you create locale-independent tests.

I've found that you can set the locale programatically, but it was not recommended (see Change language programmatically in Android). While this question is aimed at changing locale at runtime when running an app normally, I was wondering if there was a better solution to my problem.

Northampton answered 26/5, 2013 at 14:26 Comment(2)
I'm also interested in this, hence the bounty. My specifics: I'm testing from within Eclipse, and I'd like to be able to change locale, including timezone, as smoothly as possible, within a suite. I'd like to see a worked example of how to do it in an answer.Apothem
@MattGibson I updated my answer with a working example. It changes locale for the both the date formatters and the application resources.Beware
B
67

If it's just for testing, then you can change the locale programmatically without any issues. It will change the configuration of your app and you will be able to test your code with the new locale. It has the same effect as if a user has changed it. If you want to automate your tests, you can write a script that changes locale using adb shell as described here, and launch your tests afterwards.

Here is an example of testing translations of word "Cancel" for English, German and Spanish locales:

public class ResourcesTestCase extends AndroidTestCase {

    private void setLocale(String language, String country) {
        Locale locale = new Locale(language, country);
        // here we update locale for date formatters
        Locale.setDefault(locale);
        // here we update locale for app resources
        Resources res = getContext().getResources();
        Configuration config = res.getConfiguration();
        config.locale = locale;
        res.updateConfiguration(config, res.getDisplayMetrics());
    }

    public void testEnglishLocale() {
        setLocale("en", "EN");
        String cancelString = getContext().getString(R.string.cancel);
        assertEquals("Cancel", cancelString);
    }

    public void testGermanLocale() {
        setLocale("de", "DE");
        String cancelString = getContext().getString(R.string.cancel);
        assertEquals("Abbrechen", cancelString);
    }

    public void testSpanishLocale() {
        setLocale("es", "ES");
        String cancelString = getContext().getString(R.string.cancel);
        assertEquals("Cancelar", cancelString);
    }

}

Here are the execution results in Eclipse:

enter image description here

Android O update.

When running in Android O method Locale.setDefault(Category.DISPLAY, locale) shall be used (see behaviour changes for more detail).

Beware answered 16/2, 2014 at 10:46 Comment(3)
I don't extend my tests with AndroidTestCase, so I cannot get the context. I'm using @RunWith(RobolectricGradleTestRunner.class). So it seems like I cannot use the same solution. Any advice?Slider
@PKuijpers In Robolectric, you need to override the locale using qualifiers in your test Config. See robolectric.org/using-qualifiers for more information.Ofay
Please, try to reset the Locale in the tear down method, to avoid running into issues.Congratulant
A
3

The current accepted answer didn't help me. But @Dennis's comment helped to solve the Problem for me.

Use Robolectric and override the locale by specifying a resource qualifier.

Add for example @Config(qualifiers="de-port") for the German language.

@Test
@Config(qualifiers = "de-port")
fun testGetLocaleGerman(){  ...   }

Robolectrics Documentation

Alidus answered 3/12, 2021 at 14:52 Comment(0)
K
0

Without Robolectric solution:

class LocaleTestRule(
    private val locale: Locale,
) : ExternalResource() {

    override fun before() {
        val context = InstrumentationRegistry.getInstrumentation().targetContext
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            context.getSystemService(LocaleManager::class.java)
                .applicationLocales = LocaleList(locale)
        } else {
            AppCompatDelegate.setApplicationLocales(
                LocaleListCompat.create(locale)
            )
        }
    }
}

and you can use the rule as:

// or whatever language you want to restrict your test to
@get:Rule
val localeTestRule = LocaleTestRule(Locale.ENGLISH)
Khan answered 19/5 at 8:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.