How to mock CMMotionManager for Unit test

210 Views Asked by At

My codebase is using device motion data for perticular functionality. I want to unit test it. For that I need to mock CMMotionManager so that data can be manipulated and test cases can be executed according to requirements. Is there any way out to achieve this?

Callback for following function needs to be mocked

startDeviceMotionUpdates(to queue: OperationQueue, withHandler handler: @escaping CMDeviceMotionHandler)

1

There are 1 best solutions below

2
Mark O'Hara On

There are two straightforward ways you could go about mocking this;

  1. Subclassing

You can subclass CMMotionManager and overload the function that you want to mock. This will allow you to inject the mock anywhere that you currently use CMMotionManager.

class CMMotionManagerMock: CMMotionManager {

    override func startDeviceMotionUpdates(to queue: OperationQueue, withHandler handler: @escaping CMDeviceMotionHandler) {
        // Mock your desired functionality
    }
}
  1. Protocol & Extensions

When using protocols you will have to use the protocol throughout your codebase in order to be able to inject the mock when needed. This is a layer of abstraction that could be confusing if it is only used for testing purposes.

protocol DeviceMotionUpdatable {
    func startDeviceMotionUpdates(to queue: OperationQueue, withHandler handler: @escaping CMDeviceMotionHandler)
}

extension CMMotionManager: DeviceMotionUpdatable {}

// The mock

class CMMotionManagerMock: DeviceMotionUpdatable {
    func startDeviceMotionUpdates(to queue: OperationQueue, withHandler handler: @escaping CMDeviceMotionHandler) {
        // Mock your desired functionality
    }
}

// Implementation
class Some {
   let motionManager: DeviceMotionUpdatable

   init(motionManager: DeviceMotionUpdatable = CMMotionManager()) {
      self.motionManager = self.motionManager
   }
}

// Call site
Some() // Injects the real motion manager
Some(motionManager: CMMotionManagerMock()) // injects the mock motion manager