0

While trying to apply TDD to asynchronous code I found out that the same code that was working in the deployment target, didn't work in the test target. One of the examples of this problems I found using CLLocationManager:

- (void)testReceivingLocation
{
    locationManager = [[CLLocationManager alloc] init];
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    locationManager.delegate = self;
    locationManager.pausesLocationUpdatesAutomatically = NO;
    if ([CLLocationManager locationServicesEnabled])
    {
        [locationManager startUpdatingLocation];
    }
    startLocation = nil;

    NSDate *until = [NSDate dateWithTimeIntervalSinceNow:10];
    while ([until timeIntervalSinceNow] > 0)
    {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:until];
    }
    XCTAssert(alreadyReceivedLocation, @"Location wasn't received.");
}

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation
{
    alreadyReceivedLocation = true;
    // Never actually get there.
}

What can be the problem?

Sergey
  • 47,222
  • 25
  • 87
  • 129

1 Answers1

0

You should elaborate a bit more on how [SomeClass performActionWithAsyncResponse] does its job.

Supposing completionWithResult is called in a block on the main queue, this won't work because the thread ends after the the test method is finished. Which is not the case in production, because the app keeps running.

Usually I use code like this to wait for async calls which calls back on the main queue in tests.

NSDate *until = [NSDate dateWithTimeIntervalSinceNow:30];
while ([loopUntil timeIntervalSinceNow] > 0) 
{
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:until];
}

You could also stop the while loop with a condition that indicates if asynchronous work is done, using a property of the test for example.

See this post for more about asynchronous test patterns: Pattern for unit testing async queue that calls main queue on completion

Community
  • 1
  • 1
sofacoder
  • 664
  • 6
  • 19
  • Thanks for the pointer! I updated the question with the specific method I use and tried to use your code instead of sleep(). Unfortunately, nothing changed... – Sergey Sep 22 '13 at 17:34
  • Ah, CLLoactionManager I bet there simply aren't any location updates in the simulator. Can you try to run the test on a device which has a GPS fix? CLLocationManager behaves a little bit different in the simulator. – sofacoder Sep 22 '13 at 18:29