Android Espresso Intents测试随机失败,必须在使用此方法之前调用``init()

lon*_*kai 19 testing android android-espresso

我正在努力将项目推向espresso测试.我已阅读了大量文档,并按照给定的做法开始.

一切正常,但是,当谈到Intents相关测试时,结果很奇怪.

大多数情况下,测试在我的Mac中传递但在我的同事的Windows(并非所有测试都失败)中失败并显示失败消息java.lang.IllegalStateException: init() must be called prior to using this method.

非常奇怪的是,如果我们在Android Studio中运行Debug测试,则逐步执行代码,它会通过.

这是测试代码:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {

  @Rule public IntentsTestRule<MainActivity> mRule = new IntentsTestRule<>(MainActivity.class, true, false);

  AccountManager accountManager;
  MainActivity   activity;


  private void buildLoginStatus() throws AuthenticatorException {
    DanteApp app = (DanteApp) InstrumentationRegistry.getTargetContext().getApplicationContext();
    accountManager = app.getDanteAppComponent().accountManager();

    DoctorModel doctorModel = AccountMocker.mockDoctorModel();
    accountManager.save(doctorModel.doctor);
    accountManager.setAccessToken(doctorModel.access_token, false);
  }

  @Before public void before() throws Exception {
    buildLoginStatus();

    // must login
    assertThat(accountManager.hasAuthenticated(), is(true));

    activity = mRule.launchActivity(null);
    // block all of the outer intents
    intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
  }

  @After public void tearDown() throws Exception {
    accountManager.delete();
  }

  // failed
  @Test public void testViewDisplay() throws Exception {

    // check tabhost is displayed
    onView(withClassName(equalTo(TabHost.class.getName()))).check(matches(isDisplayed()));

    // check toolbar is displayed
    onView(withClassName(equalTo(ToolBar.class.getName()))).check(matches(isDisplayed()));
  }

  // passed
  @Test public void testCallServiceHotline() throws Exception {
    // switch to the account tab layout
    onView(withChild(withText(R.string.account))).perform(click());
    // click account menu to make a service call
    onView(withId(R.id.contact)).perform(click());

    // check call start expectly
    intended(allOf(
        not(isInternal()),
        hasAction(Intent.ACTION_DIAL),
        hasData(Uri.parse("tel:" + activity.getString(R.string.call_service)))
    ));
  }


  // failed
  @Test public void testOpenSettingsUI() throws Exception {
    // stub all internal intents
    Intents.intending(isInternal())
        .respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));

    onView(withChild(withText(R.string.account))).perform(click());
    onView(withId(R.id.setting)).perform(click());

    // check open settings activity successfully
    intended(anyOf(
        hasComponent(SettingActivity.class.getName())
    ));
  }
}
Run Code Online (Sandbox Code Playgroud)

测试库版本(几乎所有依赖项都是最新的,我们使用物理设备和模拟器来测试):

  • 规则:0.4.1
  • 跑步者:0.4.1
  • espresso-*:2.2.1
  • support-*:23.1.0

任何想法值得赞赏.谢谢!

Jab*_*ing 31

两种方案:

  1. 使用ActivityTestRule而不是IntentsTestRule,然后在@Before和@After中分别手动调用Intents.init()和Intents.release().
  2. 编写自定义IntentTestRule并覆盖beforeActivityLaunched()以包含您的AccountManager逻辑.将afterActivityFinished用于当前的@After逻辑.这也允许您只使用默认的IntentTestRule构造函数.(首选方案)

至于为什么会这样:

"最后在一个不相关的注释中,在使用新的IntentsTestRule时要小心.它不会初始化Intents.init(),直到活动启动后(afterActivityLaunched())." - 无耻插头到我自己的帖子(半途有用的视觉效果)

我认为你正在遇到竞争条件,在你的@Before方法中你正在执行launchActivity()然后espresso尝试intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));在你的活动实际创建之前执行,这意味着afterActivityLaunched()没有被调用,这意味着两者都没有Intents.init(),崩溃!

希望这可以帮助.


Pet*_*son 7

IntentsTestRule源自ActivityTestRule和应该管理Intents.init()Intents.release()为您服务.

但是,在我的情况下,IntentsTestRule没有正常工作.所以我在发送Intent的测试之前和之后切换回ActivityTestRule并调用. Intents.init()Intents.release()

有关详细信息,请参阅此参考.

  • 这是最干净,最直接的解决方案. (3认同)