Mocking framework-objects in Swift

Hi everyone!


I'm developing an app using TDD-practices and one of my classes are dependent on callbacks from the CoreBluetooth-framework. I've isolated everything in a way that makes my class unaware of that it is using a concrete CBCentralManager, but just an object conforming to a certain protocol. In production I have an extension on the contrete CBCentralManager to make it conform to that protocol.


However - In my test suite, I need to call some of the delegate methods of the CBCentralManager and provide objects for the callback methods. In this case a CBPeripheral. So I thought i could just instantiate, or possibly, subclass CBPeripheral, and use an instance of that and provide as argument for the callback methods.


let a = CBPeripheral() // 'init()' is unavailable

class MockCBPeripheral: CBPeripheral {}
let b = MockCBPeripheral() //' MockCBPeripheral' cannot be constructed because it has no accessible initializers

class MockCBPeripheral2: CBPeripheral {
     override init() {
          // Cannot override 'init' which has been marked unavailable
     }
}


Is there any other approach I can take to mock these classes?


Thanks in advance.

/ Albin

I'm literally working through the same issue. I can't see a way around this at the moment, but if I think of something I'll post back here. Did you come to any conclusions?

Run into same issue and here is how I solved it:


1. Create Obj - C class, that can create instance of object, from it's class name:


@implementation ObjectBuilder
+ (id)createInstanceOfClass:(NSString *)name {

    return [[NSClassFromString(name) alloc] init];
}
@end


2. Add bridging - header, so you can use this ObjectBuilder in Swift tests


3. Create instance like this:


guard let peripherial = ObjectBuilder.createInstanceOfClass("CBPeripheral") as? CBPeripheral else {
     XCTFail()
     return
}


4. In case of CBPeripherial you will receive runtime exception when its instance is dealloced - it tries to remove observer (which does not exist). To prevent that add following line:


peripherial.addObserver(peripherial, forKeyPath: "delegate", options: .New, context: nil)

Hello altac,

I think this document about Mocking in Swift might be also helpful in the topic:

https://github.com/markspanbroek/MockingInSwift


Could you please elaborate more on the topic of isolation your class from the CBCentralManager? I am very interested in the topic, because I would love to achieve such an isolation level, but I am afraid I didn't understand exactly where should I introduce the protocol you mentioned about?

Thank you in advance!

Ania

Mocking framework-objects in Swift
 
 
Q