0

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 ?

JE42
  • 4,881
  • 6
  • 41
  • 51
  • 1
    OCMock requires to call verify on your mocks to verify your expectations - have edited your question. – e1985 Apr 17 '14 at 09:36

2 Answers2

1

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];
}
e1985
  • 6,239
  • 1
  • 24
  • 39
  • I disagree. Input to "T" is an instance of "X" hence the unit test should only expect calls that are defined by "X" not by some random category, that turns out to be handy. Depending on a specific category that the implementation can freely pick makes the unit tests fragile. It also makes the reuse of the same unit tests for subclasses of X more difficult and fragile. Further, I could copy & paste the code of the category into the implementation of X and wouldn't have the issue with OCMock. – JE42 Apr 17 '14 at 20:45
  • I just didn't enter in if the idea of the category is a good or bad architecture; my answer just shows a way to test the code you expose in your question. – e1985 Apr 18 '14 at 09:32
0

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).

Carl Lindberg
  • 2,902
  • 18
  • 22