JE42
JE42

Reputation: 5159

How can I setup a mock object to fall through to the actual implementation defined in categories?

I have an objective-c class X with method turtle that I would like to mock with OCMock to unit test a class T.

//X.h

@interface X
-(void) turtle;
@end

Class T includes a category and uses that to communicate with X. The category method calls turtle.

//X+utils.h:

@interface X(Utils)
-(void) catMethod;
@end

//X+utils.m:
@implementation X(Utils)
-(void) catMethod
{
   [self turtle];
}
@end

//T.m
#import "X+utils.h"

@implementation T
-(void) useX:(X*) xInstance
{
    [xInstance catMethod];
}

In the unit test, I setup the mock such that it expects a call to turtle.

-(void) test 
{
    id mockX = [OCMockObject mockForClass:[X class]]
    [[mockX expect] turtle];

    [instanceOfT useX:mockX]; 

    [mockX verify];
}

I don't setup the mock to expect a call to the method of the category, since I would like to give the implementation the freedom to pick any category it likes to use.

The call useX is fails since OCMock catches the "unexpected" call to catMethod.

Can I configure OCMock to actually use the implementation of the category and only mock calls that are defined in the actual interface X ?

Upvotes: 0

Views: 219

Answers (2)

Carl Lindberg
Carl Lindberg

Reputation: 2972

Using -mockForClass: returns an object that will raise an exception on any method sent to it other than the ones you stub or expect. It does not actually have any of the implementations from the specified class; it just masquerades as an instance. -niceMockForClass: will not raise on unexpected methods, rather just those with a reject, but won't do anything either -- it is basically like a class with an empty implementation for every method.

As mentioned in the other answer, what you really want is a partial mock, which does use an actual instance of the class in question. All method calls will invoke the regular implementations except those that you stub, expect, or reject (and even with those, you can use -andForwardToRealObject to call the original implementations).

Upvotes: 0

e1985
e1985

Reputation: 6279

The key thing here is what you want to test.

In your test method you are testing useX:, what this method does. Taking a pure unit testing approach you should just test that it calls catMethod on x.

If you want to test that turtle is finally called you can use a partial mock like in the following code. But keep in mind this way you are not only testing T useX:, but also the catMethod declared in the category.

-(void) test 
{
    X *x = [[X alloc] init];
    id partialX = [OCMockObject partialMockForObject:x]
    [[partialX expect] turtle];

    [instanceOfT useX:x]; 

    [partialX verify];
}

Upvotes: 1

Related Questions