7

I'm trying to get mock updates from FusedLocationProviderApi, but I can't seem to make it work. This my set up method in android instrumentation test:

locationProvider = new LocationProvider(InstrumentationRegistry.getTargetContext().getApplicationContext(), settings);

// Connect first, so that we don't receive 'true' location
locationProvider.googleApiClient.blockingConnect();
// Set mock mode, to receive only mock locations
LocationServices.FusedLocationApi.setMockMode(locationProvider.googleApiClient, true);

InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
    @Override
    public void run() {
        // Start receiving locations; this call will connect api client, and if it's already connected (or after it connects) it will register for location updates
        locationProvider.start();
    }
});
// wait until we can request location updates
while (!locationProvider.isReceivingLocationUpdates) {
    Thread.sleep(10);
}

After this point I would expect any calls to LocationServices.fusedLocationApi.setMockLocation(apiClient, location) to set mock location that my listener would receive. Unfortunately this is not the case, and the listener stays silent.

My foolproof (or so I thought) method to set mock location looks like this:

private void setMockLocation(final Location location) throws Exception {
    assertTrue(locationProvider.googleApiClient.isConnected());
    assertTrue(locationProvider.isReceivingLocationUpdates);

    final CountDownLatch countDownLatch = new CountDownLatch(1);
    LocationServices.FusedLocationApi.setMockMode(locationProvider.googleApiClient, true)
            .setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(Status status) {
                    assertTrue(status.isSuccess());
                    LocationServices.FusedLocationApi.setMockLocation(locationProvider.googleApiClient, location)
                            .setResultCallback(new ResultCallback<Status>() {
                                @Override
                                public void onResult(Status status) {
                                    assertTrue(status.isSuccess());
                                    countDownLatch.countDown();
                                }
                            });
                }
            });
    assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
}

Method returns successfully, but no location is received by the listener. I'm really at a loss here. The worst part is sometimes the test would pass, but extremely randomly (to a point when the same code executed several times would pass and fail in subsequent calls). My debug manifest, for completeness, has following permissions:

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

I have allowed mock locations in settings.

wasyl
  • 3,421
  • 3
  • 27
  • 36
  • [This tutorial](https://mobiarch.wordpress.com/2012/07/17/testing-with-mock-location-data-in-android/) has good information about how to test with mock location data in Android. Also, `locationProvider` has been deprecated, you should use `GoogleApiClient` instead, you can check out this [github page](https://github.com/jbj88817/getLastLocationUsingGPS-android) for sample GoogleApiClient implementation. – ztan Sep 01 '15 at 23:05
  • I may have not been clear - `locationProvider` is my own class. I use `GoogleApiClient` and `FusedLocationProviderApi` to get location – wasyl Sep 02 '15 at 12:26
  • @wasyl I have not tried this . But have you checked whether allow mock locations options was enabled under developer options ? – manjusg Dec 24 '15 at 06:38
  • Although it is a very old question but were you able to find a solution to this? I am stuck with the same issue @wasyl – Anuj Jun 22 '16 at 06:33
  • @Anuj As far as I remember setting mock location once, and then again kind of worked (the first one being ignored), although I may remember wrong. I also opened an issue which is conveniently ignored for quite some time already - feel free to ping the maintainers https://github.com/googlesamples/android-play-location/issues/21 – wasyl Jun 22 '16 at 10:32
  • @Anuj I'm struggling with the same issue and it seems that I do everything as needed (settings of mock in my android, added permissions in manifest, set mock, set mock location, but on location changed is just never called, I've been trying for hours to find out what is the problem.. Did you manage to understand what is it? – idish Aug 09 '16 at 22:23
  • @wasyl Same question to you, I tried to set mock location multiple times every 2 seconds, but still not working. – idish Aug 09 '16 at 22:24
  • @idish Unfortunately not. Please reach out on to google team at github in the issue that I posted, maybe they'll investigate the issue further – wasyl Aug 10 '16 at 08:03

1 Answers1

2

this us an updated kotlin version. Be aware that you need to pick the app as a mock location provider in development options, and have permissions to do it set in a debug manifest file.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
    tools:ignore="ProtectedPermissions" />
</manifest>

An activity would have a val with initializer in onCreate

  private lateinit var fusedLocationClient: FusedLocationProviderClient

in oncreate()

  fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)


  fusedLocationClient.setMockMode(BuildConfig.DEBUG)

  fusedLocationClient.lastLocation
        .addOnSuccessListener { location : android.location.Location? ->
            // Got last known location. In some rare situations this can be null.
            Toast.makeText(this, location.toString(), Toast.LENGTH_SHORT).show()

        }
        .addOnFailureListener {  Toast.makeText(this, "failed", Toast.LENGTH_SHORT).show() }

and finally a way to mock a location

 private fun setMockLocation(location: android.location.Location) {
    fusedLocationClient.setMockLocation(location)
        .addOnSuccessListener { Log.d(TAG, "location mocked") }
        .addOnFailureListener { Log.d(TAG, "mock failed") }
}