在Espresso测试中模拟意图附加项

9

我正在尝试在Espresso中启动Activity。问题是我想向我用来启动ActivityIntent中添加模拟数据。以下是示例。

@RunWith(AndroidJUnit4.class)
public final class NiceActivityTester
{
    @Rule
    public final ActivityTestRule<NiceActivity> activityRule = new ActivityTestRule<>(NiceActivity.class, true, false);

    @Test
    public void justStartPlease() {
        NiceThing niceThing = Mockito.mock(NiceThing.class);
        Mockito.when(niceThing.getName()).thenReturn("Nice!");

        Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), NiceActivity.class);
        intent.putExtra("NICE_THING", niceThing);

        activityRule.launchActivity(intent);
    }
}

很不幸,Parcelable的反序列化失败了。

java.lang.RuntimeException: Unable to start activity ComponentInfo{app.application/app.application.activity.NiceActivity}: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: NiceThing_Proxy
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
    at android.app.ActivityThread.access$800(ActivityThread.java:151)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5254)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: NiceThing_Proxy
    at android.os.Parcel.readParcelableCreator(Parcel.java:2295)
    at android.os.Parcel.readParcelable(Parcel.java:2245)
    at android.os.Parcel.readValue(Parcel.java:2152)
    at android.os.Parcel.readArrayMapInternal(Parcel.java:2485)
    at android.os.BaseBundle.unparcel(BaseBundle.java:221)
    at android.os.Bundle.getParcelable(Bundle.java:755)
    at android.content.Intent.getParcelableExtra(Intent.java:5088)
    at app.application.NiceActivity.getNiceThing(NiceActivity.java:40)
    at app.application.NiceActivity.setUpToolbar(NiceActivity.java:30)
    at app.application.NiceActivity.onCreate(NiceActivity.java:20)
    at android.app.Activity.performCreate(Activity.java:5990)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
    at android.support.test.runner.MonitoringInstrumentation.callActivityOnCreate(MonitoringInstrumentation.java:534)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)

有没有办法使用模拟的extras和Intent?也许在这个领域有一些最佳实践?

我真的不想为我使用的每个模型制作构造函数,其中一些类具有十几个字段。使用Mockito监视extra或Intent无济于事。


请查看以下内容:https://google.github.io/android-testing-support-library/docs/espresso/intents/index.html(特别是“意图存根”部分)。 - piotrek1543
"Intent stubbing" 是用于在使用 Activity#startActivityForResult 时模拟结果的。但这不适用于我的情况。 - user3359826
你能解决你的问题吗?如果可以,请发布解决方案。 - hardik9850
3个回答

12

使用ActivityTestRule并覆盖getActivityIntent

在这里找到示例

public class MainActivityLaunchIntentTest {

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule =
            new ActivityTestRule<MainActivity>(MainActivity.class) {
                @Override
                protected Intent getActivityIntent() {
                    Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
                    Intent result = new Intent(targetContext, MainActivity.class);
                    result.putExtra("Name", "Earth");
                    return result;
                }
            };

    @Test
    public void shouldShowHelloEarth() {
        onView(withId(R.id.main__tv_hello)).check(matches(withText("Hello Earth!")));
    }
}

1
这个能够与最新的Android测试更新和库一起使用吗?在2018年有更好的方法来完成这个吗? - Ewoks
1
我正在尝试在onCreate()中从Intent中获取额外的内容。我想将我的Extra放入Intent中。因此,我必须存根/模拟Activity的getIntent()方法。我该如何实现这一点? - filthy_wizard
1
使用ActivityScenarioRule怎么样? - quangkid
@quangkid @filthy_wizard @Ewoks 我在上面的答案中使用了ActivityScenarioRule - Muhammad Faheem Mohd Ezani

8

以下是Kotlin解决方案:

@get:Rule
val mActivityTestRule: ActivityTestRule<TheActivity> =
        object : ActivityTestRule<TheActivity>(TheActivity::class.java) {
            override fun getActivityIntent(): Intent {
                val targetContext = InstrumentationRegistry.getInstrumentation().targetContext
                return Intent(targetContext, TheActivity::class.java).apply {
                    putExtra(TheActivity.ACTIVITY_TITLE,
                            targetContext.resources.getString(R.string.the_text))
                    putExtra(TheActivity.SETTING_TITLE,
                            targetContext.resources.getString(R.string.the_title))
                    putExtra(TheActivity.SCREEN_TYPE,
                            TheActivity.ScreenType.AMOUNT.name)
                }
            }
        }

不要忘记在 Gradle 文件中添加 androidTestImplementation 'androidx.test:rules:1.2.0' - Marfin. F

0
在最近的更新中,ActivityTestRule被宣布为废弃类。Android开发者文档建议使用ActivityScenarioActivityScenarioRule
假设我们使用ActivityScenarioRule和您的测试代码作为基本示例,那么包含一个Intent的解决方案如下:
@RunWith(AndroidJUnit4.class)
public final class NiceActivityTester
{
    @Rule
    public final ActivityScenarioRule<NiceActivity> scenarioRule = 
        new ActivityScenarioRule<>(
            new Intent(
                InstrumentationRegistry.getInstrumentation().getTargetContext(),
                NiceActivity.class)
                .putExtra("your_extra1_key", yourExtra1Value)            // first extra
                .putExtra("your_extra2_key", yourExtra2Value));          // second extra
                .putExtra("your_extra3_key", createParcelableObject())); // parcelable extra
                                                                         // you may add more

    // An example function that creates and returns a parcelable object
    ObjectType createParcelableObject() {
        ObjectType parcelableObject = new ObjectType();
        parcelableObject.setAttribute1("attribute1");
        parcelableObject.setAttribute2("attribute2");
        parcelableObject.setAttribute3("attribute3");
        .
        . // more attributes setting, for example
        .
        return parcelableObject;
    }

    @Test
    public void justStartPlease() {
        ActivityScenarioRule<NiceActivity> = scenarioRule.getScenario();

        // The rest would be your test code
        
    }
}

以下是您可能需要注意的几件事:

  • getTargetContext,或者技术上来说,InstrumentRegistry 类本身已被弃用。文档建议使用ApplicationProvider。因此,你可以将InstrumentationRegistry.getInstrumentation().getTargetContext()替换为ApplicationProvider.getApplicationContext()。无论哪种方式,在本回答发布时,我都测试并验证了两种方法的可行性。
  • 使用ActivityScenarioRule,你可以将Intent作为其参数提供。因此,在启动Activity时,可以包括一些额外的信息。只需创建一个新的Intent,并像上面的示例代码一样包含你打算启动的Activity
  • 要将一个可传输的对象成功地包含到你的Intent中,必须在实例化ActivityScenarioRule类/对象时包含该对象。在上面的示例代码中,编写了一个函数,在创建ActivityScenarioRule时创建并返回一个可传输的对象。文档明确说明该类“在测试开始前启动给定的活动,并在测试结束后关闭”。因此,无论你需要包含什么内容以供你的活动使用,都应在实例化ActivityScenarioRule时进行。

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