Objective C & OC Mock - Mocking a class method?

2019-04-10 20:03发布

问题:

I need to be able to determine whether a class method was called or not. How can I do this with OCMock?

回答1:

Starting with OCMock release 2.1 this is supported out of the box. You can now stub class methods in the same way as you stub instance methods.



回答2:

One approach is to wrap the class method in a method on your own class. So let's say your class has to call [SomeOtherClass classMethod:someString]. You could create a method invokeClassMethod: on your class like this:

-(NSString *)invokeClassMethod:(NSString *)someString {
    return [SomeOtherClass classMethod:someString];
}

Then in your test, you create a partial mock and expect invokeClassMethod:

-(void)testSomething {
    id partialMock = [OCMockObject partialMockForObject:actual];
    [[[partialMock expect] andReturn:@"foo"] invokeClassMethod:@"bar"];

    [actual doSomething:@"bar"];

    [partialMock verify];
}

If you want to verify that invokeClassMethod isn't called, you can throw an exception:

-(void)testSomethingElse {
    id partialMock = [OCMockObject partialMockForObject:actual];
    [[[partialMock stub] andThrow:[NSException exceptionWithName:@"foo" reason:@"Should not have called invokeClassMethod:" userInfo:nil] invokeClassMethod:OCMOCK_ANY];

    [actual doSomething:@"bar"];
}

The excpetion will cause the test to fail if invokeClassMethod is called.



回答3:

As zneak mentioned in their comment to your question, have a look at this answer,

And from the comments there, checkout this block-based implementation of class method swizzling.

OCMock doesnt seem to directly support what you want to do, but this solution is quite nice!



回答4:

Alternatively, lets assume that you have the class:

@interface ABCCleverClass : NSObject
+ (id)specialMethod;
@end

and you want to mock this class method. One option is to create a category on this class which defines and implements testing support. Then, you can swap the implementation of the class method in the class under test with your mock one from the category.

#import <objc/runtime.h>

@interface ABCCleverClass (TestSupport)
+ (id)mockSpecialMethod;
@end

@implementation ABCCleverClass (TestSupport)

+ (void)load {
  Method original = class_getClassMethod([ABCCleverClass class], @selector(specialMethod));
  Method mocked = class_getClassMethod([ABCCleverClass class], @selector(mockSpecialMethod));
  method_exchangeImplementations(original, mocked);
}

+ (id)mockSpecialMethod {
  // Perform mock method. You may need to add more class methods
  // in order to configure this.
}

@end