I'm developing a navigation app, based on the Here SDK. Now when I am making integration tests (androidTest), I need to simulate different GPS coordinates on the Android Emulator (Nexus 10 API 28). I tried a lot of different ways, but nothing worked (LocationManager, Fusedlocationproviderclient, ADB shell appops set mock_location, etc... The only thing that worked was ADB emu geo fix, but it's not enough...) and I didn't find anything useful about the testing of location/coordinates in the Android documentation. Is there a way to change and update the emulator GPS coordinates programmatically when the tests are running?
2 Answers
This answer works for me and is posted here for your reference. Feel free to improve on this and post as answer. I met a similar blocking point and after research on SO came up with this consolidated answer. See references at bottom for some of the SO related answers.
It includes many of the things you mentioned as trying so at a minimum this could help you identify some other unrelated cause if it still doesn ot work for you. I ran this on a Nexus 10 API 28 emulator.
Note the emulator must be running prior to running test AND your app needs to already be installed; otherwise, the gradle 'adb' command fails. (One area for improvement).
There are two parts to this: 1) updates to your build.gradle and 2) the test location mocking.
Updates to build.gradle (app)
(The task should only be executed in a debug release - since it is added as a dependency to 'assembleDebug' then I believe this is accomplished.)
// The objective of this task and supporting code is to enable mock locations
// on the target emulator device.
//
// ** REPLACE 'APP PACKAGE NAME' with your app's package name. **
// These tasks should be disabled in some way prior to release.
task enableMockLocationForTestsOnDevice(type: Exec) {
project.logger.lifecycle("------enableMockLocationForTestsOnDevice------\n")
if (true) {
project.logger.lifecycle("------enabled------\n")
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def sdkDir = properties.getProperty('sdk.dir')
def adb = "$sdkDir/platform-tools/adb"
description 'enable mock location on connected android device before executing any android test'
commandLine "$adb", 'shell', 'appops', 'set', 'APP PACKAGE NAME', 'android:mock_location', 'allow'
}
}
afterEvaluate {
// Note: the app must be already installed on device in order to this to run!
connectedDebugAndroidTest.dependsOn enableMockLocationForTestsOnDevice
connectedAndroidTest.dependsOn enableMockLocationForTestsOnDevice
}
tasks.whenTaskAdded { task ->
if (task.name == 'assembleDebug') {
task.dependsOn('enableMockLocationForTestsOnDevice')
}
}
Updates to espresso test
//...
// This rule should be familiar - it's standard part of espresso test
@Rule
public ActivityTestRule<(your activity)> mActivityRule = new ActivityTestRule<>((your activity class));
// ...
// Utility invocation in test
// start location updates - 25m/s (~55mph)
LatLng startPos = new LatLng(39.0, -77.0);
LocationUtils.startUpdates(mActivityRule.getActivity(),
new Handler(Looper.getMainLooper()),
startPos, 340, 25);
// ...
// (In a test utility class in this example: LocationUtils.java)
// Utility - uses SphericalUtil to maintain a position based on
// initial starting position, heading and movement value (in
// meters) applied every 1 second. (So a movement value
// of 25 equates to 25m/s which equates to ~55MPH)
public static void startUpdates(
final Activity activity, final Handler mHandler, final LatLng pos,
final double heading, final double movement) {
mHandler.postDelayed(new Runnable() {
private LatLng myPos = new LatLng(pos.latitude,pos.longitude);
@Override
public void run() {
Location mockLocation = new Location(LocationManager.GPS_PROVIDER); // a string
mockLocation.setLatitude(myPos.latitude); // double
mockLocation.setLongitude(myPos.longitude);
mockLocation.setAltitude(100);
mockLocation.setTime(System.currentTimeMillis());
mockLocation.setAccuracy(1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
LocationServices.getFusedLocationProviderClient(activity).setMockMode(true);
LocationServices.getFusedLocationProviderClient(activity).setMockLocation(mockLocation);
// compute next position
myPos = SphericalUtil.computeOffset(myPos, movement, heading);
mHandler.postDelayed(this, 1000);
}
}, 1000);
}
References
Gradle stuff: https://stackoverflow.com/a/39765423/2711811
Mentions why the 'adb command is necessary: https://stackoverflow.com/a/52698720/2711811
-
Alternative approach: https://stackoverflow.com/questions/52440243/how-to-write-espresso-tests-which-are-mocking-gps-locations-and-use-them-in-goog – ThomasRS Oct 23 '20 at 16:14
I used the previous code and I have the next error:
Could not get unknown property 'connectedDebugAndroidTest' for project ':X' of type org.gradle.api.Project.

- 79
- 1
- 8