Вызов + [NSBundle bundleForClass:] для частично издевательского объекта возвращает результат, отличный от неиздевательского объекта?

Я работаю над модульными тестами, используя XCTest и OCMock 2.2.1. У меня есть класс, который получает идентификатор пакета, используя:

NSString *bundleIdentifier = [[NSBundle bundleForClass:[self class]] bundleIdentifier];

Это работает, как и ожидалось, при запуске приложения или модульных тестов для этого класса, в частности.

Работая с тестами на других классах, я частично имитирую этот объект, но все еще требую, чтобы метод, который получает идентификатор пакета, работал.

То, что я вижу, до передачи экземпляра объекта в + [OCMockObject partialMockForObject:] выглядит правильно:

(lldb) po myObject
<MyObject: 0x1006ec480>
(lldb) po [NSBundle bundleForClass:[myObject class]]
NSBundle </Users/paynerc/Library/Developer/Xcode/DerivedData/xxxx/Build/Products/Debug/MyTests Tests.xctest> (loaded)
(lldb) po [[NSBundle bundleForClass:[myObject class]] bundleIdentifier]
com.paynerc.MyBundle

Однако после того, как я передам myObject в [OCMockObject partialMockForObject:myObject], все изменится:

(lldb) po myObject
<MyObject-0x1006ec480-401894396.880136: 0x1006ec480>
(lldb) po [NSBundle bundleForClass:[myObject class]]
NSBundle </Applications/Xcode.app/Contents/Developer/usr/bin> (loaded)
(lldb) po [[NSBundle bundleForClass:[myObject class]] bundleIdentifier]
nil

Тот факт, что объект изменен и включает в себя частичную магию имитации, имеет смысл. Что кажется нелогичным, так это то, почему вызов bundleForClass изменил то, что он возвращает.

Могу ли я что-нибудь сделать, чтобы bundleForClass продолжал возвращать исходное значение, за исключением издевательских вызовов внутри MyObject? Беспокойство заключается в том, что любой, кому нужен частичный макет MyObject в другом модульном тесте, должен будет не забыть предоставить заглушенную реализацию bundleForClass.

Мое текущее решение состоит в том, чтобы запросить идентификатор пакета и изучить результат. Если он равен нулю, я вызываю [NSBundle allBundles] и перебираю их, пока не найду тот, который имеет ненулевой bundleIdentifier. Хотя в настоящее время это... работает... это A) не очень надежный B) ужасный метод грубой силы и C) изменение кода приложения для поддержки модульных тестов.

Кто-нибудь еще сталкивался с этим и придумал лучшее решение?


person Ryan C. Payne    schedule 26.09.2013    source источник


Ответы (1)


Среда выполнения ведет себя правильно. Смоделированный объект является подклассом NSProxy, и, таким образом, связь во время выполнения между isa объекта и пакетом фактически разрывается (в частности, isa указывает на Class, который затем просматривается через dyld API для определения mach-o изображения из которого он был загружен и который используется для поиска пакета).

Вероятно, на прокси-сервере OCMockObject (или подклассе OCPartialMockObject) есть API, который позволяет вам получить исходный класс. Вам придется использовать это, что, конечно же, означает, что вы будете загрязнять свой код фиктивными вызовами, которые следует использовать только при тестировании.

В качестве альтернативы реализуйте метод класса для одного из классов в вашем пакете/фреймворке/независимо от того, что возвращает пакет для класса. Над этим не следует смеяться.

person bbum    schedule 26.09.2013
comment
Билл как всегда прав. Однако мы меняем поведение фиктивного объекта, чтобы он делал то, что вы ожидаете. Эта работа в процессе. См. github.com/erikdoe/ocmock/pull/45 и github.com/erikdoe/ocmock/commit/ - person Erik Doernenburg; 27.09.2013
comment
Подклассы NSProxy обычно реализуют -isKindOfClass: для возврата YES для проксируемого класса, и OCClassMockObject ничем не отличается. (Хотя, возможно, он также должен реализовывать -class, как это обычно делают подклассы NSProxy). Проблема здесь, однако, заключается в том, что исходный объект имитируется сам по себе - процесс частичного имитации изменяет его указатель isa, и поэтому возвращаемое значение из метода -class отличается, что, в свою очередь, искажает bundleForClass:. Но, как говорит Эрик, похоже, это должно быть исправлено в следующем выпуске OCMock. - person Carl Lindberg; 30.09.2013
comment
@CarlLindberg Спасибо за разъяснение; Раньше я не рассматривал реализацию OCMock*. - person bbum; 30.09.2013