Mocking or simulating CBPeripheral, CBCentralManager, etc in tests

I am developing an app that uses CoreBluetooth to communicate with a proprietary piece of hardware. I would like to be able to write tests for it, but there does not seem to be any way to mock or simulate the presence of, for example a CBPeripheral object.

This library (https://github.com/NordicSemiconductor/IOS-CoreBluetooth-Mock) almost does what I need, but it's not quite flexible enough. I think they have the right idea, but it doesn't seem to be actively maintained anymore.

This seems like a pretty big hole in the iOS SDK. Is there really not an officially supported way of testing BLE functionality?

Answered by Engineer in 804443022

There is no officially supported way of mocking BLE devices.

CoreBluetooth works with a lot of back and forth between the app, the Bluetooth stack, and the devices, and if you need to mock a proprietary device, you would need to implement the complete GATT protocol, and you would still be missing HCI level issues that might pop. So, you may find it more useful to conduct your tests with actual peripherals.


Argun Tekant /  DTS Engineer / Core Technologies

Accepted Answer

There is no officially supported way of mocking BLE devices.

CoreBluetooth works with a lot of back and forth between the app, the Bluetooth stack, and the devices, and if you need to mock a proprietary device, you would need to implement the complete GATT protocol, and you would still be missing HCI level issues that might pop. So, you may find it more useful to conduct your tests with actual peripherals.


Argun Tekant /  DTS Engineer / Core Technologies

Our QA department handles testing with physical devices. That kind of testing is also important.

The entire point of mocked objects is that you do not have to implement all of their functionality. In other frameworks you generate the mocked class with something like Mockito and then add a series of statements like this in your setup:

when(mockedObject.somefunction).thenreturn(foo)

What I'm talking about here is unit testing of a manager type class where its dependencies, such as instances of CBPeripheral and CBCentralManager are simulated or mocked in some way. We gate our PRs on successful runs of sets of unit tests and are looking for ways to increase coverage in order to reduce future maintenance headaches. The tests themselves are run automatically within the CI system. I understand that behind the scenes the CI system is running our iOS tests as integration tests on a physical iPhone sitting on a rack in a data center.

@lemessurier I would follow the practices outlined in this video: Testing Tips & Tricks

If your goal is to test your logic, this is the way to do it. If your goal is to exercise radio functionality and transmit and receive packets, you should use UI Tests and have your physical device in proximity to your CI machine.

As you have likely found out, you cannot instantiate a CBPeripheral, nor can you instantiate CBService, CBCharacteristic, and CBDescriptor.

If you're really motivated, you could explore using the mac's Bluetooth to emulate your device. You'd need to create a mechanism to control that 'Mac app' from your XCTest suite. FYI, Process isn't available on iOS, thus you can't run command line tools from an iOS XCTest suite to control your Mac app. Maybe you could get it to work with a local HTTP Server and drive it that way.

Mocking or simulating CBPeripheral, CBCentralManager, etc in tests
 
 
Q