4

我只是在掌握 TDD 和 mocking 的概念,并且在如何正确处理方面遇到了问题。我有一张下拉表格,让用户创建一个新的核心数据对象并将其保存到数据存储中。我不确定我是否采用了最好的方法来测试它。

- (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:方法并返回“是”或“否”,但鉴于问题 1,我不确定这一切的核心数据方面。

我认为我走在正确的轨道上,但我可以就如何解决我的问题使用第二种意见,并让我走上测试代码片段的正确道路。

4

2 回答 2

2

贾斯汀,

我对问题 #1 所做的是创建一个实际的 NSManagedObjectContext 但创建一个 im-memory 持久性存储。磁盘上没有任何东西,我测试了 CoreData 版本的真相。

我有一个构建 moc 并初始化持久性存储的 MWCoreDataTest 类(在我的情况下扩展为 GTMTestCase)

    - (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 上构建模拟

于 2010-02-15T18:23:50.407 回答
0

你想测试什么?您想测试 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 保存是否按预期工作,而不必关心业务逻辑。您还应该能够覆盖或模拟此方法的结果以测试业务逻辑。

我什至可能会将所有核心数据的东西移到一个单独的类中,该类将封装交互以便于模拟。

于 2010-02-15T21:00:48.013 回答