Reputation: 10608
I have some code that calls [NSBundle mainBundle]
at some point, mainly to read/set preferences. When I unit test the method, the test fails because the test's mainBundle does not contain the file.
This is a known issue, that Apple won't fix as they consider it is not a bug:
the gist of their reply is that XCTest is working correctly by returning it’s own main bundle instead of the bundle of the test target.
Previously our codebase was using a category override on NSBundle
's +mainBundle
class method to work around this. But this is dangerous because the behaviour of overriding an existing method in a category is undefined.
If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.
Actually Xcode warns you about it:
Category is implementing a method which will also be implemented by its primary class
So, what is the proper method to address the issue?
Upvotes: 6
Views: 2884
Reputation: 10608
The simplest and cleanest way of fixing this issue is to partially mock the NSBundle class in your unit tests to return [NSBundle bundleForClass:[self class]]
when you call [NSBundle mainBundle]
.
You may put this in your -setup
method so that it is mocked for your entire test class:
static id _mockNSBundle;
@implementation MyTests
- (void)setUp
{
[super setUp];
_mockNSBundle = [OCMockObject niceMockForClass:[NSBundle class]];
NSBundle *correctMainBundle = [NSBundle bundleForClass:self.class];
[[[[_mockNSBundle stub] classMethod] andReturn:correctMainBundle] mainBundle];
}
@end
Nice and clean.
[Source]
Upvotes: 10