Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically simulate GPS location in iOS tests

I would like to write UI tests in Swift that make screenshots of various places of a map in our app. In order to do this, I need to simulate fake GPS data during the test.

There are some solutions like this one (https://blackpixel.com/writing/2016/05/simulating-locations-with-xcode-revisited.html) that use GPX files and simulate the location with Debug > Simulate Location in Xcode, but I need this to be completely automated. Ideal would be something similar to the LocationManager in Android.

like image 691
Nbfour Avatar asked Aug 25 '17 07:08

Nbfour


People also ask

How do you simulate location on iPhone?

With your iPhone or iPad plugged in, select Toolbox at the top of the program, and then VirtualLocation from that screen. Select somewhere on the map, or use the search bar, to choose where you want to fake your location. Select Modify virtual location, and then select OK when you see the confirmation prompt.

How do I simulate a drive in Xcode?

Here are the steps for using GPX file: Click on the Active Scheme section link. Select Edit Scheme. On the Run Scheme > Options tab, tick the Allow Location Simulation box. To import GPX into the software, open the Xcode main menu and choose Debug / Simulate Location / Add GPX File to Workspace.


1 Answers

I have had similar problems when writing UI tests, because the simulator / tethered device can't do everything you might want. What I do is write mocks that mimic the desired behavior (of something I would normally have no control over).

Substituting a custom location manager for the CLLocationManager will allow you to take full control of location updates, as you can programmatically send the location updates through the CLLocationManagerDelegate method: locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]).

Create a class MyLocationManager, make it a subclass of CLLocationManager, and have it override all the methods you call. Don't call super in the overridden methods, as CLLocationManager should never actually receive a method call.

class MyLocationManager: CLLocationManager {
  override func requestWhenInUseAuthorization() {
    // Do nothing.
  }

  override func startUpdatingLocation() {
    // Begin location updates. You can use a timer to regularly send the didUpdateLocations method to the delegate, cycling through an array of type CLLocation.
  }

  // Override all other methods used.

}

The delegate property doesn't need to be overridden (and can't be), but you have access to it as a subclass of CLLocationManager.

To use MyLocationManager you should pass in launch arguments that tell your app if it is a UITest or not. In your test case's setUp method insert this line of code:

app.launchArguments.append("is_ui_testing")

Store CLLocationManager as a property that is a MyLocationManager when testing. When not testing CLLocationManager will be used as normal.

static var locationManger: CLLocationManager = ProcessInfo.processInfo.arguments.contains("is_ui_testing") ? MyLocationManager() : CLLocationManager()
like image 190
N Brown Avatar answered Nov 15 '22 08:11

N Brown