OCMStub sends OCMConstraint instance to to real method upon stub creation

827 views Asked by At

I'm trying to test the URL/path against a request is (or would be) made from a REST-client class using OCMock. The client uses RestKit (which uses AFNetworking) for the communication. Therefore my plan was to:

  1. Create a block which checks if a AFHTTPRequestOperation URL is the desired one.
  2. Create a partial mock of the of AFHTTPClient.
  3. Mock (stub) the enqueueHTTPRequestOperation: method with the block (of 1.).
  4. Create a mock of RKObjectManager and set its HTTPClient property to the AFHTTPClient partial-mock (of 2.).
  5. Init an instance of my client-class with the mock-manager (from 4.).
  6. Invoke the method of this instance of which the URL is to be checked.
  7. Verify that enqueueHTTPRequestOperation: was invoked.

I'm not sure if I'm getting OCMock wrong because I couldn't find examples on mocking methods that take one or more arguments like I need to. ...never the less, this is how I tried to achieve the goal:

    void (^checkBlock)(NSInvocation *) = ^(NSInvocation *invocation) {

        AFHTTPRequestOperation *requestOperation = nil;
        [invocation getArgument:&requestOperation atIndex:0];

        XCTAssert(requestOperation != nil);

        NSString *path = requestOperation.request.URL.path;
        XCTAssert([path containsString:@"/rest/user/authenticate"]);
    };

    AFHTTPClient *httpClientMock = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:FAServerUrl]];
    OCMPartialMock(httpClientMock);

    [OCMStub([httpClientMock enqueueHTTPRequestOperation:[OCMArg isNotNil]]) andDo:checkBlock];

    RKObjectManager *objectManager = OCMClassMock([RKObjectManager class]);

    [OCMStub([objectManager HTTPClient]) andReturn:httpClientMock];

    FAUserClient *userClient = [[FAUserClient alloc] initWithUser:nil objectManager:objectManager];

    [userClient getAccessTokenForUsername:@"testuser"
                                 password:@"pass123"
                                  success:^(NSString *token, NSArray *roles) {
                                  }
                                  failure:^(NSInteger errorCode, NSError *error) {                                          
                                  }];

    OCMVerify([httpClientMock enqueueHTTPRequestOperation:OCMOCK_ANY]);

But on [OCMStub([httpClientMock enqueueHTTPRequestOperation:[OCMArg isNotNil]]) andDo:checkBlock]; I get an EXC_BAD_ACCESS (code=1). Apparently creating the mock-stub (with OCMStub) invokes the to be stubbed method, with the given [OCMArg isNotNil]. I thought A: the parameter just has a declarative meaning and B: this creates a stub and does not invoke the method right away.

Any help or suggestions leading into the "right" direction would be greatly appreciated.

EDIT:

As well tried:

    OCMStub([httpClientMock enqueueHTTPRequestOperation:[OCMArg checkWithBlock:^BOOL(id obj) {
        AFHTTPRequestOperation *request = (AFHTTPRequestOperation *)obj;
        NSString *path = request.request.URL.path;
        XCTAssert([path containsString:@"/rest/user/authenticate"]);
        return YES;
    }]]);

...with the same "result".

Best, gabriel

1

There are 1 answers

2
Ben Flynn On BEST ANSWER

Edit

Looked more closely. You are calling OCMPartialMock(httpClientMock). This does not convert the object you call it on, it returns a partial mock. Capture the result in a variable.

AFHTTPClient *httpClientMock = OCPartialMock([[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:FAServerUrl]]);

You should still make the change noted below in your andDo: block. You can also use the "modern" syntax for this:

OCMStub([myObject myMethod]).andDo(myBlock);

Original

I think the issue might be the code in the andDo: block.

[invocation getArgument:&requestOperation atIndex:0];

For all Objective-C methods, NSInvocation has two default arguments, self and _cmd at indexes 0 and 1. Try getting the argument at index 2.

You might also consider including NSInvocation+OCMAdditions which gives you getArgumentAtIndexAsObject . Another alternative is using [OCMArg checkWithBlock:] in which the arg is handed to your evaluation block directly.