Why does my test fail with "expected method was not invoked" when using a real parameter but pass when using `[OCArg isNotNil]`?

StackOverflow https://stackoverflow.com/questions/21212459

  •  29-09-2022
  •  | 
  •  

Question

I'm testing a method using OCMock. The method is as follows:

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(NSDictionary *)parameters
                        block:(void (^)(id responseObject, NSError *error))block
{
    return [self GET:URLString parameters:parameters success:^(NSURLSessionDataTask * __unused task, id responseObject) {
        block(responseObject, nil);
    } failure:^(NSURLSessionDataTask * __unused task, NSError *error) {
        block(nil, error);
    }];
}

This test fails with "expected method was not invoked":

id sessionManagerPartialMock = [OCMockObject partialMockForObject:[FOOHTTPSessionManager manager]];

NSString *URLstring = @"test";
NSDictionary *parameters = nil;

void (^block)(id, NSError *) = ^(id responseObject, NSError *error) {};

void (^successBlock)(NSURLSessionDataTask *, id) = ^(NSURLSessionDataTask * __unused task, id responseObject) {
    block(responseObject, nil);
};

void (^failureBlock)(NSURLSessionDataTask *, NSError *) = ^(NSURLSessionDataTask * __unused task, NSError *error) {
    block(nil, error);
};

[[sessionManagerPartialMock expect] GET:URLstring parameters:parameters success:successBlock failure:failureBlock];

[sessionManagerPartialMock GET:URLstring parameters:parameters block:block];

[sessionManagerPartialMock verify];
[sessionManagerPartialMock stopMocking];

But this passes:

id sessionManagerPartialMock = [OCMockObject partialMockForObject:[FOOHTTPSessionManager manager]];

NSString *URLstring = @"test";
NSDictionary *parameters = nil;

void (^block)(id, NSError *) = ^(id responseObject, NSError *error) {};

[[sessionManagerPartialMock expect] GET:URLstring parameters:parameters success:[OCMArg isNotNil] failure:[OCMArg isNotNil]];

[sessionManagerPartialMock GET:URLstring parameters:parameters block:block];

[sessionManagerPartialMock verify];
[sessionManagerPartialMock stopMocking];

Why does the first test fail and how can I make it pass?

I've put an example project on GitHub demonstrating the issue: https://github.com/paulyoung/OCMockExample

Was it helpful?

Solution

As others have said, blocks are compared by comparing their addresses. Two different blocks - even though they do the exact same thing - are not equal. You can still make your test more specific by actually invoking the success and failure block, and check if they behave as expected.

As far as I understand you want to test that

  • the method calls another method with two blocks, that when invoked, invoke the original block with
  • some response object as the first and nil as the second argument in case of success
  • nil as the first, an NSError* as the second argument in case of failure

Here is the test for the success case:

id responseMock = @"responseObject";
__block BOOL blockCalled = NO;

void (^block)(id, NSError *) = ^(id responseObject, NSError *error) {
    XCTAssertNil(error, @"");
    XCTAssertEqualObjects(responseObject, responseMock, @"");
    blockCalled = YES;
};

[[[sessionManagerPartialMock expect] andDo:^(NSInvocation *invocation) {
    void (^successBlock)(NSURLSessionDataTask *, id);
    [invocation getArgument:&successBlock atIndex:4];
    successBlock((id)@"task", responseMock);
}] GET:URLstring parameters:parameters success:[OCMArg isNotNil] failure:[OCMArg isNotNil]];

[sessionManagerPartialMock GET:URLstring parameters:parameters block:block];

[sessionManagerPartialMock verify];
XCTAssert(blockCalled, @"");

OTHER TIPS

I checked your repo and I think now I can give you good answer.

What you are trying to do in your test is blocks comparing and currently this is not possible in Obj-c world. Even if you block from test and block created inside singleton looks identical they are not the same blocks.

You can fix this by using you fail and success block as private properties and use them in your OCMock expect. Like :

[[sessionManagerPartialMock expect] GET:URLstring parameters:parameters success:sessionManager.successBlock failure:sessionManager.failureBlock];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top