6

I am trying to test creation of CLLocationManager as a singletone with default parameters:

+ (GeolocationService *)defaultGeolocationService
{
    static GeolocationService *_defaultGeolocationService = nil;

    static dispatch_once_t oncePredicate;

    dispatch_once(&oncePredicate, ^{
        _defaultGeolocationService = [[GeolocationService alloc] init];
        [_defaultGeolocationService initLocationManager];
    });

    return _defaultGeolocationService;
}

- (void)initLocationManager
{
    self.locationManager = [CLLocationManager new];
    self.locationManager.delegate = self;
    self.locationManager.pausesLocationUpdatesAutomatically = YES;
    [self.locationManager requestAlwaysAuthorization];
}

Test looks like this:

- (void)testInitWithDefaultsSettings
{
    GeolocationService *defaultGeolocationService = [GeolocationService defaultGeolocationService];

    XCTAssertTrue(defaultGeolocationService.settings.autoPause, @"autoPause");
}

And I get an exception: *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Non-UI clients cannont be auto paused'

What should I do to make this test work?

Nadzeya
  • 641
  • 6
  • 16

3 Answers3

6

The issue is with the following line

self.locationManager.pausesLocationUpdatesAutomatically = YES;

This property cannot be set on an application that is not running a UI (not sure why, I could not find any documentation about it).

You may want to do some checks in your test to ensure that the UI has loaded before initialising your GeoLocationService and setting self.locationManager.pausesLocationUpdatesAutomatically.

Matthew Cawley
  • 2,828
  • 1
  • 31
  • 42
  • Also, `self.locationManager.allowsBackgroundLocationUpdates` causes an error that says, `Invalid parameter not satisfying: !stayUp || CLClientIsBackgroundable(internal->fClient) || _CFMZEnabled() (NSInternalInconsistencyException).` This is understandable because these are properties associated with a UI device. – William Grand Apr 02 '21 at 16:21
0

I searched a bit on simple and fast solution to avoid it and I have found some workaround to this problem I think you have three solutions :

- Easy One :

Create a Preprocessor Macro for Testing Target. But it's depend on how many Macro you have and can complicate the code management if you abuse on it.

- Medium one :

You know that all unit test are running on a simulator so why not try a

#if !(TARGET_IPHONE_SIMULATOR)

But you will tell me that you want as well to run the app on your simulator and if you do only that case you won't be able to use the property on the app launching case in the simulator.

So why not add a complementary test about knowing what XPCServiceName is running like below :

NSString *serviceName = [NSProcessInfo processInfo].environment[@"XPC_SERVICE_NAME"];
BOOL amITesting = ([serviceName rangeOfString:@"xctest"].location != NSNotFound);

So with #if !(TARGET_IPHONE_SIMULATOR) && !amITesting you can use your

self.locationManager.pausesLocationUpdatesAutomatically = YES;

And Test will pass.

- Third one :

I don't like this solution but I write it as well, it's the monkey solution... If you don't want to add a Pre-Processor Macro, add a try catch and test will pass as well. But I don't advice you to use that one.

I hope it will helps a bit more.

Mohamed.A.A
  • 344
  • 3
  • 5
0

This is a restriction of unit tests and other non-UI processes, reason only known to Apple. Here is a solution I am using:

let serviceName = ProcessInfo.processInfo.environment["XPC_SERVICE_NAME"]
let testing = serviceName?.hasSuffix("xctest") ?? false

if !testing {
    locationManager.pausesLocationUpdatesAutomatically = true
}
picciano
  • 22,341
  • 9
  • 69
  • 82