Я играю с использованием Core Data для управления графом объектов, главным образом для внедрения зависимостей (подмножество NSManagedObjects должно сохраняться, но это не главное в моем вопросе).Когда я запускаю модульные тесты, я хочу взять на себя создание NSManagedObjects, заменив их на mocks.
У меня сейчас есть подходящее средство для этого, которое заключается в использовании метода method_exchangeImplementations во время выполнения для обмена [NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:]
с моей собственной реализацией (т. е. возвращая издевается).Это работает для небольшого теста, который я сделал.
У меня есть два вопроса по этому поводу:
- Есть ли лучший способ заменить создание объекта Core Data, чем swizzling insertNewObjectForEntityForName: inManagedObjectContext?Я не углубился в среду выполнения или Core Data и, возможно, упускаю что-то очевидное.
- Моя концепция метода создания замещающего объекта состоит в том, чтобы возвращать ложные NSManagedObjects.Я использую OCMock, который не будет напрямую насмехаться над подклассами NSManagedObject из-за их динамических
@property
s.Пока клиенты моего NSManagedObject общаются с протоколами, а не с конкретными объектами, поэтому я возвращаю проверенные протоколы, а не конкретные объекты.Есть ли лучший способ?
Вот некоторый псевдоишный код, чтобы проиллюстрировать то, к чему я стремлюсь.Вот класс, который я мог бы тестировать:
@interface ClassUnderTest : NSObject
- (id) initWithAnObject:(Thingy *)anObject anotherObject:(Thingo *)anotherObject;
@end
@interface ClassUnderTest()
@property (strong, nonatomic, readonly) Thingy *myThingy;
@property (strong, nonatomic, readonly) Thingo *myThingo;
@end
@implementation ClassUnderTest
@synthesize myThingy = _myThingy, myThingo = _myThingo;
- (id) initWithAnObject:(Thingy *)anObject anotherObject:(Thingo *)anotherObject {
if((self = [super init])) {
_myThingy = anObject;
_myThingo = anotherObject;
}
return self;
}
@end
Я решил создать подклассы Thingy и Thingo NSManagedObject, возможно, для сохранения и т. Д., Но также я могу заменить init чем-то вроде:
@interface ClassUnderTest : NSObject
- (id) initWithManageObjectContext:(NSManagedObjectContext *)context;
@end
@implementation ClassUnderTest
@synthesize myThingy = managedObjectContext= _managedObjectContext, _myThingy, myThingo = _myThingo;
- (id) initWithManageObjectContext:(NSManagedObjectContext *)context {
if((self = [super init])) {
_managedObjectContext = context;
_myThingy = [NSEntityDescription insertNewObjectForEntityForName:@"Thingy" inManagedObjectContext:context];
_myThingo = [NSEntityDescription insertNewObjectForEntityForName:@"Thingo" inManagedObjectContext:context];
}
return self;
}
@end
Затем в своих модульных тестах я могу сделать что-то вроде:
- (void)setUp {
Class entityDescrClass = [NSEntityDescription class];
Method originalMethod = class_getClassMethod(entityDescrClass, @selector(insertNewObjectForEntityForName:inManagedObjectContext:));
Method newMethod = class_getClassMethod([FakeEntityDescription class], @selector(insertNewObjectForEntityForName:inManagedObjectContext:));
method_exchangeImplementations(originalMethod, newMethod);
}
... где мой []FakeEntityDescription insertNewObjectForEntityForName:inManagedObjectContext]
возвращает макеты вместо реальных NSManagedObjects (или протоколов, которые они реализуют).Цель only этих проверок состоит в том, чтобы проверять вызовы, сделанные им во время модульного тестирования ClassUnderTest.Все возвращаемые значения будут заглушены (включая любые методы получения, ссылающиеся на другие NSManagedObjects).
Мои тестовые ClassUnderTest
экземпляры будут созданы в модульных тестах, таким образом:
ClassUnderTest *testObject = [ClassUnderTest initWithManagedObjectContext:mockContext];
(контекст на самом деле не будет использоваться в тесте, из-за моего крутого insertNewObjectForEntityForName:inManagedObjectContext
)
Смысл всего этого?В любом случае, я собираюсь использовать Core Data для многих классов, поэтому я мог бы также использовать его, чтобы уменьшить нагрузку, связанную с управлением изменениями в конструкторах (каждое изменение в конструкторе включает редактирование всех клиентов, включая несколько модульных тестов).Если бы я не использовал Базовые данные, я мог бы рассмотреть что-то вроде Возражение .