Извикването на + [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 от който е зареден и това се използва за намиране на пакета).

Вероятно има API на OCMockObject прокси (или подклас OCPartialMockObject), който ви позволява да извлечете оригиналния клас. Ще трябва да използвате това, което, разбира се, означава, че ще замърсявате кода си с фалшиви извиквания, които трябва да се използват само при тестване.

Като алтернатива, внедрете метод на клас в един от класовете във вашия пакет/рамка/каквото и да е, който връща пакета за класа. Това не бива да се подиграва.

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