OCMock с проблемой динамических свойств Core Data

Я использую OCMock для имитации некоторых объектов Core Data. Раньше у меня были свойства, реализованные с помощью явных методов доступа в стиле Objective-C 1.0:

// -- Old Core Data object header
@interface MyItem : NSManagedObject {}
- (NSString *) PDFName;
- (void) setPDFName:(NSString *)pdfName;
@end

// -- implementation provides generated implementations for both getter and setter

Теперь я переместил код в Objective-C 2.0 и хочу воспользоваться преимуществами нового синтаксиса @property и реализации динамически генерируемых методов для объектов Core Data:

// -- New Core Data object header
@interface MyItem : NSManagedObject {}
@property (nonatomic, retain) NSString *PDFName;
@end

// -- Core Data object implementation
@implementation MyItem
@dynamic PDFName;
@end

Однако теперь, когда я создаю фиктивный элемент, он, похоже, не обрабатывает динамические свойства:

// -- creating the mock item
id mockItem = [OCMockObject mockForClass:[MyItem class]];
[[[mockItem stub] andReturn:@"fakepath.pdf"] PDFName]; // <-- throws exception here

Ошибка выглядит следующим образом:

Test Case '-[MyItem_Test testMyItem]' started.
2009-12-09 11:47:39.044 MyApp[82120:903] NSExceptionHandler has recorded the following exception:
NSInvalidArgumentException -- *** -[NSProxy doesNotRecognizeSelector:PDFName] called!
Stack trace: 0x916a4d24 0x92115509 0x97879138 0x978790aa 0x9090cb09 0x97820db6 0x97820982 0x10d97ff 0x10d9834 0x9782005d 0x9781ffc8 0x20103d66 0x20103e8c 0x20103642 0x20107024 0x20103642 0x20107024 0x20103642 0x20105bfe 0x907fead9 0x977e4edb 0x977e2864 0x977e2691 0x90877ad9 0xbf565 0xbf154 0x107715 0x1076c3 0x1082e4 0x89d9b 0x8a1e5 0x894eb 0x907e81c7 0x978019a9 0x978013da 0x907dd094 0x907ea471 0x9478c7bd 0x9478c1b9 0x94784535 0x5ede 0x326a 0x5
Unknown.m:0: error: -[MyItem_Test testMyItem] : *** -[NSProxy doesNotRecognizeSelector:PDFName] called!

Делаю что-то не так? Есть ли другой способ смоделировать основные данные/объект с @динамическими свойствами?


person amrox    schedule 09.12.2009    source источник


Ответы (3)


Также ответил на ваш перекрестный пост на Форум OCMock

Посетите http://iamleeg.blogspot.com/2009/09/unit-testing-core-data-driven-apps.html.

По сути, он предлагает абстрагировать интерфейс вашего объекта Core Data для протокола и использовать этот протокол вместо класса, в котором вы передаете экземпляры вашего объекта основных данных.

Я делаю это для своих основных объектов данных. Затем вы можете использовать mockForProtocol:

id mockItem = [OCMockObject mockForProtocol:@protocol(MyItemInterface)];
[[[mockItem expect] andReturn:@"fakepath.pdf"] PDFName];

Работает отлично! Он также предлагает создать неосновную фиктивную реализацию интерфейса данных, которая просто синтезирует свойства:

@implementation MockMyItem
@synthesize PDFName;
@end

...

id <MyItemInterface> myItemStub = [[MockMyItem alloc] init] autorelease];
[myItem setPDFName:@"fakepath.pdf"];

Я тоже использовал это, но я не уверен, что это что-то добавляет по сравнению с подходом mockForProtocol:/stub: , и это еще одна вещь, которую нужно поддерживать.

person Christopher Pickslay    schedule 07.05.2010
comment
Просто для полноты картины, не могли бы вы повторно опубликовать свой ответ здесь, пожалуйста. Это не ответ. Что, если эта ссылка умрет? - person Bach; 22.11.2010
comment
Я не понимаю, почему бы просто не использовать настоящий NSManagedObject в своем тесте? Есть ли у вас другие тесты, подтверждающие соответствие протокола вашей управляемой объектной модели? - person ImHuntingWabbits; 06.11.2013
comment
Я больше этого не делаю. Гораздо проще просто создать хранилище в памяти и использовать настоящие объекты CD. На момент публикации этого вопроса использование Core Data в модульных тестах было очень неуклюжим, но Apple значительно улучшила поддержку модульного тестирования за последние несколько лет. - person Christopher Pickslay; 06.11.2013

Приведенный выше ответ меня не удовлетворил, потому что мне не нравилось создавать для этого протокол. Так я узнал, что есть более простой способ сделать это. Вместо

[[[mockItem stub] andReturn:@"fakepath.pdf"] PDFName]; // <-- throws exception here

Просто пиши

[[[mockItem stub] andReturn:@"fakepath.pdf"] valueForKey:@"PDFName"];
person user2963938    schedule 07.11.2013

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

Лично я нашел способ сделать его легким:

Создайте простую категорию, например, внутри вашего файла модульного тестирования, непосредственно перед вашим классом модульного тестирования:

@implementation MyItem(UnitTesing)
- (NSString *)PDFName{return nil;};
@end

Кроме того, вы можете сохранить его в отдельном файле, но убедитесь, что этот файл не является частью вашей рабочей задачи. Вот почему я предпочитаю хранить его в том же тестовом файле, где я хочу его использовать.

Огромное преимущество этого метода в том, что вам не нужно копировать методы, созданные XCode для поддержки отношений. Также вы можете поместить в эту категорию только те методы, которые собираетесь вызывать в своих тестах.

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

- (void)setPDFName:(NSString *)name{};
person Slabko    schedule 23.11.2013