Question

I'd like to run some tests on a view controller involving CLBeacons. Unfortunately, while CLBeacons are not themselves a private class, all the necessary properties on them are read-only, without any write accessors.

The methods are written in a manner that should help maximize testability, but without the ability to generate the CLBeacons, how do I test my view controller's logic regarding them?

Edit:

To explain the scope of my purpose, I'm experiencing some odd behavior in the app at times. I want to try and confirm the source of the odd behavior is in a specific area of code by testing various scenarios and confirming that various other related components in my code work correctly. (E. g. I can test that my data handling works correctly, therefore I'll know it's the animation / layout code that is misbehaving).

Was it helpful?

Solution

I have done this by using OCMockito with XCTest.

CLBeacon *mockBeacon = mock([CLBeacon class]);

Then I can use this to call the delegate methods on the class that is the CoreLocation delegate. The test might look like this:

- (void)testDidRangeOnABeacon
{
    MyLocationDelegate *myDelegate = [[MyLocationDelegate alloc] init];

    CLLocationManager *mockManager = mock([CLLocationManager class]);
    CLBeacon *mockBeacon  = mock([CLBeacon class]);
    CLBeaconRegion *mockRegion  = mock([CLBeaconRegion class]);


    [myDelegate locationManager:mockManager
                didRangeBeacons:@[mockBeacon]
                       inRegion:mockRegion];


    // XCTAsserts...
    XCTAssert([myDelegate.checkSomethingAboutRanging]);
}

OTHER TIPS

Since you cannot create CLBeacon instances directly, maybe you could refactor your methods to take as parameter a CustomBeacon, defined like this:

@interface CustomBeacon : NSObject

@property (nonatomic, strong) NSNumber *major;
@property (nonatomic, strong) NSNumber *minor;
@property (nonatomic, assign) CLProximity proximity;
// plus all other fields in CLBeacon..

+ (instancetype) customBeaconWithBeacon:(CLBeacon *) beacon;
- (instancetype) initWithBeacon:(CLBeacon *) beacon;

@end

Then you can simply use [[CustomBeacon customBeaconWithBeacon:realBeacon] instead of realBeacon when you are dealing with CLBeacon (after monitoring), and in your test you can instantiate your CustomBeacon instances directly.

Not as clean as using CLBeacon, but this is the best I could think of regarding testability.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top