I'm new to OCMock and trying to unit test a camera application that uses AVCaptureSession.
I'm trying to unit test the method that captures a still image and passes it along.
I'm having trouble mocking AVCaptureStillImageOutput's captureStillImageAsynchronouslyFromConnection:completionHandler:
. The issue is in passing a CMSampleBufferRef
to the completionHandler. I don't really care what the contents of the CMSampleBufferRef is other than it can't be nil/null. With the unit test case, the only time it is referenced in the completion handler is if ( imageDataSampleBuffer ) {
...every other use I have mocked.
Here is what I've tried:
Setup:
NSError *error = nil;
CMSampleBufferRef imageDataSampleBuffer;
stillImageOutputMock = OCMStrictClassMock([AVCaptureStillImageOutput class]);
Try #1:
[[stillImageOutputMock expect] captureStillImageAsynchronouslyFromConnection:connectionMock
completionHandler:([OCMArg invokeBlockWithArgs:imageDataSampleBuffer, &error, nil])];
Gives compiler error:
/Users/.../UnitTests/Unit/CameraViewController/CameraViewControllerTests.m:195:152: Implicit conversion of C pointer type 'CMSampleBufferRef' (aka 'struct opaqueCMSampleBuffer *') to Objective-C pointer type 'id' requires a bridged cast
Xcode offers to "fix" it with this: (which I tried; try #2)
[[stillImageOutputMock expect] captureStillImageAsynchronouslyFromConnection:connectionMock
completionHandler:([OCMArg invokeBlockWithArgs:(__bridge id)(imageDataSampleBuffer), &error, nil])];
But this generates a EXC_BAD_ACCESS even though I've mocked all methods that actually use the imageDataSampleBuffer inside completionHandler. The exception is coming from in OCMock where it is adding the imageDataSampleBuffer to an array of args
+ (id)invokeBlockWithArgs:(id)first,... NS_REQUIRES_NIL_TERMINATION
{
NSMutableArray *params = [NSMutableArray array];
va_list args;
if(first)
{
[params addObject:first]; <<<<<< EXCEPTION HERE. first isn't an object
va_start(args, first);
Try #3:
OCMock documentation states non-object arguments must be wrapped in value objects and the expression must be wrapped in round brackets.
, so I tried:
[[stillImageOutputMock expect] captureStillImageAsynchronouslyFromConnection:connectionMock
completionHandler:([OCMArg invokeBlockWithArgs:@(imageDataSampleBuffer), &error, nil])];
but the compiler complains:
/Users/.../UnitTests/Unit/CameraViewController/CameraViewControllerTests.m:196:119: Illegal type 'CMSampleBufferRef' (aka 'struct opaqueCMSampleBuffer *') used in a boxed expression
Suggestions?
I was able to get it running with [OCMArg invokeBlock]
like:
[[stillImageOutputMock expect] captureStillImageAsynchronouslyFromConnection:connectionMock
completionHandler:[OCMArg invokeBlock]];
but then the completion handler gets 0x0 for imageDataSampleBuffer and all of the interesting functionality in the completion handler is skipped.
Ah, I found the solution digging into the OCMock tests (specifically OCMockObjectTests.m)
OCMOCK_VALUE() did the trick. Here is the working syntax:
NSError *error = nil;
CMSampleBufferRef imageDataSampleBuffer;
stillImageOutputMock = OCMStrictClassMock([AVCaptureStillImageOutput class]);
[[stillImageOutputMock expect] captureStillImageAsynchronouslyFromConnection:connectionMock
completionHandler:([OCMArg invokeBlockWithArgs:OCMOCK_VALUE(imageDataSampleBuffer), OCMOCK_VALUE(&error), nil])];