Я очень иду по прежним направлениям - довольно строгий подход к тестированию.

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

У меня есть подходящее средство сделать это сейчас, которое заключается в использовании метода method_exchangeImplementations во время выполнения для обмена[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:] с моей собственной реализацией (т. е. возвращая издевается). Это работает для небольшого теста, который я сделал.

У меня есть два вопроса по этому поводу:

Есть ли лучший способ заменить создание объекта Core Data, чем swizzling insertNewObjectForEntityForName: inManagedObjectContext? Я не углубился в среду выполнения или Core Data и, возможно, упускаю что-то очевидное.моя концепция метода создания замещающего объекта состоит в том, чтобы возвращать ложные объекты NSManagedObjects. Я использую OCMock, который не будет напрямую издеваться над подклассами NSManagedObject из-за их динамического@propertys. Пока клиенты моего 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, я мог бы рассмотреть что-то вродевозражение.

Ответы на вопрос(4)

Ваш ответ на вопрос