Android Espresso Intents test randomly crashes with `` init () must be called before using this method``

I am currently working on sending a project for espresso testing. I read a bunch of documents and follow the instructions to get started.

Everything works fine, however, when it comes to the test related to Intents, the result looks strange.

In most cases, the tests passed on my Mac, but in my Windows colleague (not all tests fail) with the java.lang.IllegalStateException: init() must be called prior to using this method error message java.lang.IllegalStateException: init() must be called prior to using this method .

Oddly enough, if we run the Debug test in Android Studio, the step-by-step code step will go through it.

here is the test code:

 @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()) )); } } 

Test library version (almost all the dependencies are updated, and we use both physical devices and an emulator for testing):

  • rule: 0.4.1
  • runner: 0.4.1
  • espresso- *: 2.2.1
  • support- *: 23.1.0

Any idea deserves appreciation. Thanks!

+6
source share
2 answers

Two solutions:

  • Use an ActivityTestRule instead of an IntentsTestRule, and then manually call Intents.init () and Intents.release () in @Before and @After, respectively.
  • Write a custom IntentTestRule and override beforeActivityLaunched () to enable the AccountManager logic. Use afterActivityFinished for the current current @After logic. It will also allow you to use the standard IntentTestRule constructor. (Preferred Solution)

How does this happen:

"Finally, on an unrelated note, be careful when using the new IntentsTestRule. It does not initialize Intents.init () until the action (afterActivityLaunched ()) begins." - Shameless plugin for my own message (half useful visual)

I think you encountered a race condition when in your @Before method you launch launchActivity (), then espresso tries to execute intending(not(isInternal())).respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null)); before your activity is actually created, which means that afterActivityLaunched() not called, which means there is no Intents.init() , crash!

Hope this helps.

+15
source

IntentsTestRule derived from an ActivityTestRule and should manage Intents.init() and Intents.release() for you.

However, in my case, the IntentsTestRule did not work correctly. So I go back to ActivityTestRule and call Intents.init() before and Intents.release() after the test that Intent sent.

For more information, see the link.

0
source

All Articles