XCTest не работает при вызове [NSBundle mainBundle]

У меня есть код, который в какой-то момент вызывает [NSBundle mainBundle], в основном для чтения/установки настроек. Когда я модульно тестирую метод, тест не проходит, потому что mainBundle теста не содержит файл.

Это известная проблема, которую Apple победила. не исправить, поскольку они считают, что это не ошибка :

Суть их ответа в том, что XCTest работает правильно, возвращая свой собственный основной пакет вместо пакета тестовой цели.

Ранее наша кодовая база использовала переопределение категории в методе класса NSBundle +mainBundle, чтобы обойти это. Но это опасно, поскольку переопределение существующего метода в категории не определено.

Если имя метода, объявленного в категории, совпадает с именем метода исходного класса или метода другой категории того же класса (или даже суперкласса), поведение не определено в отношении того, какая реализация метода используется в данный момент. время выполнения.

На самом деле Xcode предупреждает вас об этом:

Категория реализует метод, который также будет реализован его основным классом.

Итак, каков правильный метод решения проблемы?


person KPM    schedule 11.03.2015    source источник
comment
XCTest не и никогда не возвращал тестовый пакет для +[NSBundle mainBundle]. (То же самое с OCUnit.) Я не уверен, откуда пришла идея, что это действительно так — связанная страница больше не доступна, поэтому я не вижу, есть ли на ней дополнительные ссылки — но, насколько я знаю, мы никогда не говорили кто-либо иначе.   -  person Chris Hanson    schedule 16.07.2015


Ответы (1)


Самый простой и чистый способ решить эту проблему — частично имитировать класс NSBundle в ваших модульных тестах, чтобы возвращать [NSBundle bundleForClass:[self class]] при вызове [NSBundle mainBundle].

Вы можете поместить это в свой метод -setup, чтобы он имитировался для всего вашего тестового класса:

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

Красиво и чисто.

[Источник]

person KPM    schedule 11.03.2015
comment
Поскольку mockNSBundle является статическим, почему бы не использовать статический метод +setup? - person OliverD; 19.11.2015
comment
Как это сделать в Свифте? - person Ramis; 25.05.2016
comment
@Ramis Ну, в Swift вы бы все равно не использовали OCMock. Вместо этого вам нужно будет реорганизовать свой код для использования внедрения кода, а затем в своем тестовом классе вы создадите подкласс NSBundle и переопределите функцию. - person KPM; 25.08.2016