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?
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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With