Android espresso and account picker
Asked Answered
N

3

8

I am having trouble making the instrumentation test using the Espresso. I have an activity where account picker is popup-ed when app is started (main activity). If customer clicks on cancel (in dialog), picker is popup up again; If user clicks on add, the result is picked up on activity result.

I dont know how to create a simple test with espresso which will include that picker. When I create the Instrumentation test with the MainActivity, I got this message: No activities in stage RESUMED...

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity>{
    MainActivity myActivity;
    public MainActivityTest(){
        super(MainActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        getActivity();
    }

    public void testAccountPicker(){
        onView(withText("Choose an account")).check(matches(isDisplayed()));
    }
}

Does anybody had similar problem?

Thanx for your answers in advance.

Nasopharynx answered 11/2, 2014 at 13:31 Comment(2)
Could you post your activity lifecycle methods?Artis
Bolhoso, I just having this in onCreate method: Intent googlePicker = AccountPicker.newChooseAccountIntent(null, null, new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, true, null, null, null, null); startActivityForResult(googlePicker, Utils.PICK_ACCOUNT_REQUEST); And logic for account selected.. Other life-cycle methods are not changed.Nasopharynx
A
3

That's a tough one :). The problem here is that once the flow leaves your application (Google Account Picker is an external application), Espresso ends the test. Account Picker is an activity from the package com.google.android.gms, thus external. Once it's started, your test is finished and you'll never be able to match anything in the dialog.

You have three possible solutions to make your tests feasible:

  • Using classpath substitution on your app to fake the intents; or
  • Fixing your app "testability"; or
  • Using dependency injection, like Dagger

I'll show how to use classpath substitution. The technique is really simple: you should isolate your Intent creation in a separate class, say IntentsFactory and, during tests, override that class.

Say your factory is in com.yourapp.factories.IntentsFactory and it is something like this:

public class IntentsFactory {
    public static Intent getAccountPickerIntent (Context context) {
        return AccountPicker.newChooseAccountIntent(null, null, new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, true, null, null, null, null);
    }
}

You should create in your test app (say it is com.yourapp.tests) a package with the same name and methods, but that returns a different Intent, a mocked/dummy one:

public class IntentsFactory {
    public static Intent getAccountPickerIntent (Context context) {
        return new Intent(context, MyDummyAccountPickerActivity.class);
    }
}

Whenever your tests execute, they will use the "nearest" class in the classpath, that is, the IntentsFactory from your tests. Instead of returning an intent that send the flow to another app, the flow will go to an class of your project and Espresso won't end the tests.

The only caveat here is that you'll have to create the MyDummyAccountPickerActivity which will return a result and a Bundle similar to the one returned by the framework class. The activity should exists in your app's manifest and you'll have to instruct your emulator Dalvik runtime to allow classpath (check it out this this and this links) substitution with the following command line:

adb shell setprop dalvik.vm.dexopt-flags v=n,o=v
adb shell stop installd
adb shell start installd

And execute your tests.

I had a similar problem to test the Camera and it's thoroughly discussed in Espresso forum

Artis answered 18/2, 2014 at 12:8 Comment(1)
Like always in 'needs to be done yesterday' i left the test on pause for a while. But i understand what you are suggesting, will try it for sure. Sorry for the late response. ThanxNasopharynx
E
1

Seems, that you must operate on a root view which in your case the "account picker". Try this out:

public void testAccountPicker(){
    onView(withText("Choose an account"))
        .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
        .check(matches(isDisplayed()));
}
Enrollment answered 12/2, 2014 at 7:43 Comment(3)
Hi Denys, I still got the error message: " No activities in stage RESUMED. Did you forget to launch the activity. (test.getActivity() or similar)?"Nasopharynx
OK, you have not your custom account picker inside the app but Google account picker, right? In that case I think you are not able to deal with it as you are outside of your app. Ask this question in android-test-kit-discuss Google group - groups.google.com/forum/#!forum/android-test-kit-discuss.Enrollment
Yes, you are correct. I use the google account picker but not my own. Thanx for the response.Nasopharynx
M
0

There are several ways that you might be able to go about testing this using Espresso Intents https://google.github.io/android-testing-support-library/docs/espresso/intents/

You can verify that the Intent was sent to open the account picker by using the intended() syntax. You can also verify the behavior of your activity with the returned result from the picker using the intending().respondWith() syntax.

If you really want to interact with the picker directly, you may be able to using the UIAutomator API: https://developer.android.com/topic/libraries/testing-support-library/index.html#UIAutomator

UIAutomator can be used inside of Espresso tests.

Magus answered 26/5, 2017 at 17:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.