Я очень иду по прежним направлениям - довольно строгий подход к тестированию.
аю с использованием Core Data для управления графом объектов, главным образом для внедрения зависимостей (подмножество NSManagedObjects действительно необходимо сохранить, но это не является предметом моего вопроса). При запуске модульных тестов я хочу взять на себя создание NSManagedObjects, заменив их на ложные.
У меня есть подходящее средство сделать это сейчас, которое заключается в использовании метода 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]
возвращает mocks вместо реальных NSManagedObjects (или протоколов, которые они реализуют).только Цель этих проверок состоит в том, чтобы проверять вызовы, сделанные им во время модульного тестирования ClassUnderTest. Все возвращаемые значения будут заглушены (включая любые методы получения, ссылающиеся на другие NSManagedObjects).
Мой тестClassUnderTest
экземпляры будут созданы в модульных тестах, таким образом:
ClassUnderTest *testObject = [ClassUnderTest initWithManagedObjectContext:mockContext];
(контекст на самом деле не будет использоваться в тесте, из-за моегоinsertNewObjectForEntityForName:inManagedObjectContext
)
Смысл всего этого? В любом случае, я собираюсь использовать Core Data для многих классов, поэтому я мог бы также использовать его, чтобы уменьшить нагрузку, связанную с управлением изменениями в конструкторах (каждое изменение в конструкторе включает редактирование всех клиентов, включая несколько модульных тестов). Если бы я не использовал Core Data, я мог бы рассмотреть что-то вродевозражение.