Тестирование метода контроллера с OCMock и Core Data - PullRequest
4 голосов
/ 15 февраля 2010

Я просто понимаю концепции TDD и насмешек, и сталкиваюсь с вопросом, как правильно. У меня есть лист, который выпадает и позволяет пользователю создать новый объект основных данных и сохранить его в хранилище данных. Я не уверен, что я использую лучший подход к тестированию.

- (IBAction)add:(id)sender 
{  
  NSString *itemName = [self.itemNameTextField stringValue];
  SGItem *newItem = [NSEntityDescription insertNewObjectForEntityForName:kItemEntityName inManagedObjectContext:[self managedObjectContext]];
  newItem.name = itemName;

  NSError *error = nil;
  BOOL canSaveNewItem = [[self managedObjectContext] save:&error];
  if (!canSaveNewItem) 
  {
    [NSApp presentError:error]; 
  }

  [self clearFormFields];  // Private method that clears text fields, disables buttons
  [NSApp endSheet:[self window] returnCode:NSOKButton];
}

Я пытаюсь написать два метода тестирования, чтобы проверить это: один, который проверяет сценарий, в котором управляемый объект не может быть сохранен, и один, где он успешно сохраняет.

@interface SGAddItemWindowControllerTests : SGTestCase 
{
@private
  SGAddItemWindowController *addItemWindowController;
  id mockApp;
  id mockNameField;
}

- (void)setUp 
{
  mockNameField = [OCMockObject mockForClass:[NSTextField class]];
  mockApp = [OCMockObject mockForClass:[NSApplication class]];

  addItemWindowController = [[BLAddItemWindowController alloc] init];  
  [addItemWindowController setValue:mockNameField forKey:@"itemNameTextField"];
}

- (void)testAddingNewItemFromSheetFailed
{
  // Setup
  NSString *fakeName = @"";
  [[[mockNameField expect] andReturn:fakeName] stringValue];
  [[mockApp expect] presentError:[OCMArg any]];

  // Execute
  [addItemWindowController add:nil];

  // Verify
  [mockApp verify];
}

- (void)testAddingNewItemFromSheetSucceeds
{
  // Setup
  NSString *fakeName = @"Item Name";
  [[[mockNameField expect] andReturn:fakeName] stringValue];
  [[mockApp expect] endSheet:[OCMArg any] returnCode:NSOKButton];

  // Execute
  [addItemWindowController add:nil];

  // Verify
  [mockApp verify];
  [mockNameField verify];
}

@end

Вот проблемы, которые я знаю, но у меня нет уверенности, как их решить:

  1. Я не уверен, как обращаться с контекстом управляемого объекта с точки зрения теста. Должен ли я вызвать весь стек данных ядра или просто создать макет NSManagedObjectContext?
  2. Идея просто установить значения текстового поля как способ запуска оператора if кажется неправильной. В идеале я думаю, что должен заглушить метод save: и вернуть YES или NO, но, учитывая вопрос 1, я не уверен насчет аспектов Core Data всего этого.

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

Ответы [ 2 ]

2 голосов
/ 15 февраля 2010

Justin

То, что я делаю для вопроса № 1, - это создаю фактический NSManagedObjectContext, но создаю постоянное хранилище в памяти. Ничто не попадает на диск, и я проверяю версию истины CoreData.

У меня есть класс MWCoreDataTest (расширяется в моем случае GTMTestCase), который создает moc и инициализирует хранилище постоянных данных

    - (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }

    return managedObjectContext;
}



- (NSPersistentStoreCoordinator*)persistentStoreCoordinator;
{
    if (persistentStoreCoordinator) return persistentStoreCoordinator;
    NSError* error = nil;
    NSManagedObjectModel *mom = [self managedObjectModel];
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
                                  initWithManagedObjectModel:mom];


    if (![persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType
                                                  configuration:nil
                                                            URL:nil
                                                        options:nil
                                                          error:&error]) {
        [[NSApplication sharedApplication] presentError:error];
        return nil;
    }
    return persistentStoreCoordinator;
}

WRT # 2, я думаю, что все в порядке - если вы планируете тестировать более одного поведения в классе, переместите

[addItemWindowController setValue:mockNameField forKey:@"itemNameTextField"];

в метод testAdding ..

Если вы решите # 1, тогда вы можете просто установить в поле itemNameText значение nil, и ваша проверка сохранения сработает.

WRT # 3, я бы подтвердил, что построение макета на NSApp === построение макета на NSApplication

0 голосов
/ 16 февраля 2010

Что вы хотите проверить? Вы хотите проверить, что Core Data сохраняет или нет? Или вы хотите проверить, правильно ли ваше приложение реагирует на результат вызова CoreData?

В любом случае, я думаю, вам следует извлечь метод, который выполняет сохранение в соответствии с:

-(BOOL)saveNewItem:(NSString *)itemName error:(NSError **)error { 
    SGItem *newItem = [NSEntityDescription insertNewObjectForEntityForName:kItemEntityName inManagedObjectContext:[self managedObjectContext]];
  newItem.name = itemName;

  NSError *error = nil;
  return[[self managedObjectContext] save:&error];
}

- (IBAction)add:(id)sender {  
  NSString *itemName = [self.itemNameTextField stringValue];
  NSError *error = nil;
  BOOL canSaveNewItem = [self saveNewItem:itemName error:&error];
  if (!canSaveNewItem) {
    [NSApp presentError:error]; 
  }

  [self clearFormFields];  // Private method that clears text fields, disables buttons
  [NSApp endSheet:[self window] returnCode:NSOKButton];
}

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

Я бы, возможно, даже переместил бы весь материал Core Data в отдельный класс, который бы инкапсулировал взаимодействие для облегчения насмешек.

...