0

I have the following UIImage category:

@implementation UIImage (Exception)

+ (nullable UIImage *)imageCanThrowWithData:(NSData *)data error:(NSError **)errorPtr
{
    UIImage *image = nil;

    @try {
        image = [self imageWithData:data];
    } @catch (id exception) {
        *errorPtr = [NSError errorWithDomain:@"mydomain" code:0 userInfo:nil];
    }

    return image;
}

@end

And then I am trying to test this with OCMock:

excerpt from setup ...

beforeEach(^{
        NSString* labelPath = [[NSBundle mainBundle] pathForResource:@"image" ofType:@"jpg"];
        imageData = [NSData dataWithContentsOfFile:labelPath];
        image = [UIImage imageWithData:imageData];
    });


it(@"should return the image if no exception is thrown", ^{
        id mock = OCMStrictClassMock([UIImage class]);

        NSError *error = nil;

        OCMStub([mock imageWithData:OCMOCK_ANY]).andReturn(image);

        OCMStub([mock imageMightThrowWithData:imageData error:&error]).andReturn([UIImage imageMightThrowWithData:imageData error:&error]);

        UIImage* resultImage = [[mock expect] imageMightThrowWithData:imageData error:&error];
        expect(resultImage).toNot(beNil());
        expect(error).to(beNil());

        [mock stopMocking];
    });

Why is resultImage is nil. Notes:
I am not an expert on OCMock, so I might be doing something naive.
UIImage imageWithData can throw an exception in case you are dealing with core data external storage.

Francesco Puglisi
  • 2,140
  • 2
  • 18
  • 26
Zsolt
  • 3,648
  • 3
  • 32
  • 47

2 Answers2

0

You are mocking the method +imageMightThrowWithData:error:, but then trying to call the mocked method as part of the mocked implementation. I'm not sure what the result would be, but it wouldn't surprise me if that just failed and returned nil. You are using OCMStrictClassMock, which means you can no longer call any class methods on UIImage, including your own.

However, since the imageData is already supplied as an argument, you don't need to mock it at all -- just pass the argument you want to test with. In fact, you don't need to mock +imageWithData: either. Just pass the imageData you already seem to have. I could possibly see mocking +imageWithData: to throw with a non-strict mock, in order to test that case (if you can't easily get an NSData which will cause the Apple method to throw in the first place), but for this test I don't see the need to use OCMock at all. So just remove the OCMStrictClassMock and the two OCMStub calls and I think it should work.

If you want to mock out the imageWithData: method so it returns faster, I guess that should work, but use OCMClassMock and not a strict one, so that you can still test your own class method, and then don't OCMStub the method you're trying to test.

Carl Lindberg
  • 2,902
  • 18
  • 22
0

You don't normally create a stub or an expectation for the method that you're trying to test.

If I understand the purpose of your unit test correctly, i.e. check that the returned value is not nil when there are no exceptions, you could follow these steps:

  1. Stub +[UIImage imageWithData:] to return a UIImage object.
  2. Call +[UIImage imageCanThrowWithData: error:] and save the returned value in a local variable.
  3. Check that the returned value is not nil.
id imageMock = OCMClassMock([UIImage class]);
OCMStub([imageMock imageWithData:OCMOCK_ANY]).andReturn(imageMock);
NSData *data = OCMClassMock([NSData class]);
UIImage *result = [UIImage imageCanThrowWithData:data error:nil];
XCTAssertNotNil(result);

Note: It's good practice to have only one assertion per test, so I would suggest creating a separate one to check that the error is nil when no exceptions are thrown.

Francesco Puglisi
  • 2,140
  • 2
  • 18
  • 26