在执行 Espresso 测试时更改语言环境

9
我正在创建一个简单的布局,应该支持阿拉伯语和RTL布局。一切都正常工作。现在我想编写Espresso测试并断言它是否实际显示了翻译后的文本。例如,对于阿拉伯语,它应该显示来自arabic strings.xml的文本。
到目前为止,我尝试了以下代码作为TestRule。
public void setLocale(Locale locale) {
        Resources resources = InstrumentationRegistry.getTargetContext().getResources();
        Locale.setDefault(locale);
        Configuration config = resources.getConfiguration();
        config.locale = locale;
        resources.updateConfiguration(config, resources.getDisplayMetrics());
    }

以上代码更改了布局方向,但不会从本地化目录中加载资源。
我没有做任何额外的事情,类似于http://www.andreamaglie.com/2016/a-test-rule-for-setting-device-locale/这样的内容。
我错过了什么吗?

你如何构建 locale - azizbekian
@azizbekian 喜欢这个 Locale("ar", "AE")。 - Hardik Trivedi
2个回答

7

我已创建一个小型测试项目,使用了您提供的链接,用于美国和英国,主要类在回答后面,但这是一个公共项目,所以您可以下载它
对于“AE”,您需要在values-ar-rAE下创建一个strings.xml请参见此链接)。
编辑:为每种语言和MyActions类添加了另一个测试。
Credits: MyActions来自这里,测试示例来自这里,您可能需要从开发人员设置中停止动画(来自第二个链接和这里)。

ForceLocaleRule:

import android.content.res.Configuration;
import android.content.res.Resources;
import android.support.test.InstrumentationRegistry;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

import java.util.Locale;

public class ForceLocaleRule implements TestRule {

    private final Locale testLocale;
    private Locale deviceLocale;

    public ForceLocaleRule(Locale testLocale) {
        this.testLocale = testLocale;
    }

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            public void evaluate() throws Throwable {
                try {
                    if (testLocale != null) {
                        deviceLocale = Locale.getDefault();
                        setLocale(testLocale);
                    }

                    base.evaluate();
                } finally {
                    if (deviceLocale != null) {
                        setLocale(deviceLocale);
                    }
                }
            }
        };
    }

    public void setLocale(Locale locale) {
        Resources resources = InstrumentationRegistry.getInstrumentation().getTargetContext().getResources();
        Locale.setDefault(locale);
        Configuration config = resources.getConfiguration();
        config.locale = locale;
        resources.updateConfiguration(config, resources.getDisplayMetrics());
    }

}

美国测试:

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Locale;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static example.com.testlocale.MyActions.setTextInTextView;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;

@RunWith(AndroidJUnit4.class)
public class MainActivityUsTest {

    @ClassRule
    public static final ForceLocaleRule localeTestRule = new ForceLocaleRule(Locale.US);

    @Rule
    public ActivityTestRule<MainActivity> myActivityRule = new ActivityTestRule<>(MainActivity.class);

    private Context context;

    @Before
    public void setUp() {
        context = InstrumentationRegistry.getTargetContext();
    }

    @Test
    public void testAirplaneEn() {
        assertEquals("airplane", context.getString(R.string.airplane));
    }

    @Test
    public void testAirplaneEnOnView() {
        onView(withId(R.id.text_view))
                .perform(setTextInTextView(context.getString(R.string.airplane)),
                        closeSoftKeyboard());
        onView(withId(R.id.text_view))
                .check(matches(withText(containsString("airplane"))));
    }

}

英国测试:

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.Locale;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static example.com.testlocale.MyActions.setTextInTextView;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;

@RunWith(AndroidJUnit4.class)
public class MainActivityGbTest {

    @ClassRule
    public static final ForceLocaleRule localeTestRule = new ForceLocaleRule(Locale.UK);

    @Rule
    public ActivityTestRule<MainActivity> myActivityRule = new ActivityTestRule<>(MainActivity.class);

    private Context context;

    @Before
    public void setUp() {
        context = InstrumentationRegistry.getTargetContext();
    }

    @Test
    public void testAirplaneEnGB() {
        assertEquals("aeroplane", context.getString(R.string.airplane));
    }

    @Test
    public void testAirplaneEnOnView() {
        onView(withId(R.id.text_view))
                .perform(setTextInTextView(context.getString(R.string.airplane)),
                        closeSoftKeyboard());
        onView(withId(R.id.text_view))
                .check(matches(withText(containsString("aeroplane"))));
    }

}

我的操作:

import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.view.View;
import android.widget.TextView;

import org.hamcrest.Matcher;

import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static org.hamcrest.CoreMatchers.allOf;

public class MyActions {

    public static ViewAction setTextInTextView(final String value){
        return new ViewAction() {
            @SuppressWarnings("unchecked")
            @Override
            public Matcher<View> getConstraints() {
                return allOf(isDisplayed(), isAssignableFrom(TextView.class));
            }

            @Override
            public void perform(UiController uiController, View view) {
                ((TextView) view).setText(value);
            }

            @Override
            public String getDescription() {
                return "set text to TextView";
            }
        };
    }

}

values-en-rUS\strings.xml

<resources>
    <string name="app_name">TestLocale</string>
    <string name="airplane">airplane</string>
</resources>

values-en-rGB\strings.xml

<resources>
    <string name="app_name">TestLocale</string>
    <string name="airplane">aeroplane</string>
</resources>

谢谢您的查看。一切看起来都很好。但是,如果我对显示来自strings.xml的值的视图进行断言,它是否能够正常工作呢?例如,假设有一个片段/活动,它在某个TextView上显示@string/airplane。如果我使用Espresso进行视图断言,它是否会通过此强制语言环境获取正确的区域设置? - Hardik Trivedi
@HardikTrivedi 我已经编辑了我的回答并增加了更多的测试,似乎它不会加载原始文本,但一旦你设置了文本就是正确的。希望这正是你要找的。 - MikeL
如果你正在使用androidx,可以参考这个答案(我没有进行过测试):@TaiNguyen。 - MikeL
@MikeL 谢谢你的回答,但我不明白它是如何工作的...我的意思是,我不知道在你的方法下在哪里设置语言。 - Tai Nguyen
@TaiNguyen他引用了另一个答案。我建议你去那里看看,或者在那个答案中添加评论以获得澄清。 - MikeL
显示剩余4条评论

0
对于任何在切换到androidx后遇到Locale更改问题的人:目前androidx.appcompat:appcompat:1.1.0存在缺陷,但可以通过降级到1.0.2或在AppCompatActivity中添加@0101100101的解决方案来解决。在Espresso单元测试中进行了测试,一切正常。
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
    if (overrideConfiguration != null) {
        int uiMode = overrideConfiguration.uiMode;
        overrideConfiguration.setTo(getBaseContext().getResources().getConfiguration());
        overrideConfiguration.uiMode = uiMode;
    }
    super.applyOverrideConfiguration(overrideConfiguration);
}

参考 https://dev59.com/HFMI5IYBdhLWcg3w0e97#58004553


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接