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

Android Espresso意向测试随机失败,在使用此方法之前必须调用``init()``,android,testing,android-espresso,Android,Testing,Android Espresso,我目前正致力于将一个项目推进到浓缩咖啡测试中。我已经阅读了大量文档,并按照给定的练习开始 一切正常,然而,当涉及到意向相关测试时,结果却很奇怪 大多数情况下,测试在我的Mac电脑中通过,但在我同事的Windows中失败(并非所有测试都失败),并显示失败消息java.lang.IllegalStateException:init(),在使用此方法之前必须先调用 非常奇怪的是,如果我们在androidstudio中一步一步地运行调试测试,代码就会通过 以下是测试代码: @RunWith(Androi

我目前正致力于将一个项目推进到浓缩咖啡测试中。我已经阅读了大量文档,并按照给定的练习开始

一切正常,然而,当涉及到意向相关测试时,结果却很奇怪

大多数情况下,测试在我的Mac电脑中通过,但在我同事的Windows中失败(并非所有测试都失败),并显示失败消息
java.lang.IllegalStateException:init(),在使用此方法之前必须先调用

非常奇怪的是,如果我们在androidstudio中一步一步地运行调试测试,代码就会通过

以下是测试代码:

@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())
    ));
  }
}
@RunWith(AndroidJUnit4.class)
@最大的
公共类维护活动测试{
@Rule public IntentsTestRule mRule=新的IntentsTestRule(MainActivity.class,true,false);
会计经理会计经理;
主要活动;
私有void buildLoginStatus()引发AuthenticateTorException{
DanteApp=(DanteApp)InstrumentationRegistry.getTargetContext().getApplicationContext();
accountManager=app.getDanteAppComponent().accountManager();
DoctorModel DoctorModel=AccountMocker.mockDoctorModel();
accountManager.save(doctorModel.doctor);
accountManager.setAccessToken(doctorModel.access\u token,false);
}
@Before public void Before()引发异常{
buildLoginStatus();
//必须登录
断言(accountManager.hasAuthenticated(),为(true));
activity=mRule.launchActivity(空);
//阻止所有外部意图
意图(不是(isInternal()).respondWith(新的Instrumentation.ActivityResult(Activity.RESULT_OK,null));
}
@在public void tearDown()之后引发异常{
accountManager.delete();
}
//失败
@Test public void testViewDisplay()引发异常{
//检查是否显示tabhost
onView(使用类名(equalTo(TabHost.class.getName()))).check(匹配项(isDisplayed()));
//显示“检查”工具栏
onView(使用类名(equalTo(ToolBar.class.getName()))。检查(匹配项(isDisplayed()));
}
//通过
@Test public void testCallServiceHotline()引发异常{
//切换到帐户选项卡布局
onView(withChild(withText(R.string.account))).perform(click());
//单击“帐户”菜单进行服务呼叫
onView(带id(R.id.contact)).perform(单击());
//检查呼叫启动是否正常
预期(全部)(
不是(isInternal()),
hasAction(意图、行动和拨号),
hasData(Uri.parse(“tel:+activity.getString(R.string.call_服务)))
));
}
//失败
@Test public void testOpenSettingsUI()引发异常{
//存根所有内部意图
意图。意图(isInternal())
.响应(新的Instrumentation.ActivityResult(Activity.RESULT_OK,null));
onView(withChild(withText(R.string.account))).perform(click());
onView(带id(R.id.setting))。执行(单击());
//成功选中“打开设置”活动
有意的(
hasComponent(SettingActivity.class.getName())
));
}
}
测试库版本(几乎所有依赖项都是最新的,我们使用物理设备和模拟器进行测试):

  • 规则:0.4.1
  • 跑步者:0.4.1
  • 浓缩咖啡-*:2.2.1
  • 支架-*:23.1.0
任何想法都值得赞赏。谢谢

两种解决方案:

  • 使用ActivityTestRule而不是IntentsTestRule,然后在@Before和@After中分别手动调用Intents.init()和Intents.release()
  • 在ActivityLaunched()之前编写一个自定义的intettestrule并重写,以包含AccountManager逻辑。将afterActivityFinished用于当前的@After逻辑。这还允许您只使用默认的intettestrule构造函数。(首选解决方案)
  • 至于发生这种情况的原因:

    最后,请注意,在使用新的IntentsTestRule时要小心。在启动活动(afterActivityLaunched())之前,它不会初始化Intents.init()

    我认为您正在运行一种竞争条件,在@Before方法中,您正在执行launchActivity(),然后espresso尝试执行
    intenting(而不是(isInternal())。respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK,null))在实际创建活动之前,这意味着未调用
    afterActivityLaunched()
    ,这意味着
    也未调用Intents.init()
    ,崩溃


    希望这能有所帮助。

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

    但是,在我的例子中,
    IntentsTestRule
    不能正常工作。因此,我切换回
    ActivityTestRule
    并在发送意图的测试之前和之后调用
    Intents.init()


    有关更多信息,请参见此项。

    这是最干净、最直接的解决方案。