Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XCTest fails when calling [NSBundle mainBundle]

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?

like image 290
KPM Avatar asked Mar 11 '15 17:03

KPM


1 Answers

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]

like image 164
KPM Avatar answered Sep 18 '22 21:09

KPM