How to write Espresso Tests which are mocking GPS

2019-02-28 01:29发布

问题:

I recorded an espresso test with Espresso Recorder. I want to test some location changes in my app.

Currently I'm mocking the location with this code:

LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setAccuracy( Criteria.ACCURACY_FINE );

String mocLocationProvider = LocationManager.GPS_PROVIDER;//lm.getBestProvider( criteria, true );

lm.addTestProvider(mocLocationProvider, false, false,
        false, false, true, true, true, 0, 5);
lm.setTestProviderEnabled(mocLocationProvider, true);

Location loc = new Location(mocLocationProvider);
Location mockLocation = new Location(mocLocationProvider); // a string
mockLocation.setLatitude(-26.902038);  // double
mockLocation.setLongitude(-48.671337);
mockLocation.setAltitude(loc.getAltitude());
mockLocation.setTime(System.currentTimeMillis());
mockLocation.setAccuracy(1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
    mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
lm.setTestProviderLocation( mocLocationProvider, mockLocation);

I also added the permission to the debug manifest file:

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>

But unluckily I still get a Security Exception:

java.lang.SecurityException: mypackage.test from uid not allowed to perform MOCK_LOCATION

I want to run the recorded test case with the mocked location in Google Firebase Test Lab. How can I solve this problem?

回答1:

Basically you have to enable the application in the devs options (select mock location app). Since you cannot control devices on google plateform, you have to use an ADB command to enable it.

appops set {yourPackageName} android:mock_location allow

As for example, this is what i am doing in a @before function for my mocking location tests :

@Before
fun grantPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        with(getInstrumentation().uiAutomation) {
            ...
            executeShellCommand("appops set " + InstrumentationRegistry.getTargetContext().packageName + " android:mock_location allow")
            Thread.sleep(1000)
            ...
        }
    }
}

Based on this approach, note that you can also create a snippet for your gradle task to do it automatically on your workstation if you plug any new device. See for example : How to set Allow Mock Location on Android Device before executing AndroidTest with uiautomator and espresso?

Hope this help !