7

I'm trying to deal with OCMock. I created simple class MyClass.

@interface MyClass : NSObject
- (NSString *)simpleMethod;
@end

@implementation MyClass

- (NSString *)simpleMethod {
    [self method];
    return @"simple";
}

- (void)method {
    NSLog(@"ABC");
}

@end

What I want to check is if method method was invoked when simpleMethod has been called. Now I've got following code but it doesn't work:

- (void)testMethodInvoked
{
    id mock = [OCMockObject mockForClass:[MyClass class]];
    [[mock stub] simpleMethod];

    SEL selector = NSSelectorFromString(@"method");
    [[mock expect] methodForSelector:selector];
    [mock verify];
}

How should I test this case? I think that is pretty easy to do, but I have no idea how solve this problem.

How to create mock and call method simpleMethod which invoke method method?

Current log:

<unknown>:0: error: -[OCMockTestTests testOne] : OCMockObject[MyClass]: expected method was not invoked: methodForSelector:@selector(method)
Tomasz Szulc
  • 4,217
  • 4
  • 43
  • 79

2 Answers2

16

You never actually create an object of the class that you want to test. Also, you have to expect first, then invoke the method:

- (void)testMethodInvoked
{
    // first create an object that you want to test:
    MyClass *object = [[MyClass alloc] init];
    // create a partial mock for that object
    id mock = [OCMockObject partialMockForObject:object];
    // tell the mock object what you expect
    [[mock expect] method];
    // call the actual method on the mock object
    [mock simpleMethod];
    // and finally verify
    [mock verify];
}
Sebastian
  • 7,670
  • 5
  • 38
  • 50
  • Thank you but the problem is that `method` is private method, not visible outside class `MyClass` – Tomasz Szulc Nov 30 '13 at 22:47
  • 4
    Doesn't sound like an appropriate condition for a unit test, then @TomaszSzulc. Unit tests are for interfaces -- external behavior -- not implementations. – jscs Nov 30 '13 at 23:16
  • 1
    @TomaszSzulc You can solve this by creating a private category on the `MyClass` in the `XCTestCase` subclass. In this category, you expose the private methods of `MyClass` to the `XCTestCase` subclass. – Bart Jacobs Oct 24 '14 at 10:59
4

I sometimes find it useful to test "private" methods / implementations -- perhaps don't call it a unit test if that breaks some kind of orthodoxy -- but for a complex implementation I may want to verify behavior on a more granular level than through the external interface.

In any event, I will expose class extension methods by creating a category in the test class:

@interface MyClass (ExposeForTest)
- (void)method;
@end

- (void)testMyClass
{
   id mock = [OCMockObject mockForClass:MyClass.class];
   [[mock expect] method];
   [mock simpleMethod];
}
Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
  • Technically this is a class extension, not a category. But you are correct this is the cleanest way to expose private methods in test cases. – Christopher Pickslay Dec 05 '13 at 07:08
  • @ChristopherPickslay Debatable, yes? Class extensions use the syntax `@interface MyClass()` - also referred to as anonymous categories. But, since we don't provide the implementation, as we do with named categories, I can see where you are coming from. https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html – Ben Flynn Dec 05 '13 at 16:24