2011-04-06 78 views
1

我正在为较大的iOS应用程序编写Core Data ContextManager。 ContextManager提供了一个NSManagedContext,当其他ContextManagers将其NSMangedContext保存到持久数据存储区时,它将自动更新。核心数据mergeChangesFromContextDidSaveNotification无效

我有一个单元测试(TestContextManager),创建两个上下文,将一个对象添加到一个,并测试以查看该对象是否出现在另一个上下文中。它没有。 为什么最后一次测试失败?

以下是ContextManager的代码和失败的单元测试。单元测试中的最后一个断言失败。每隔一个断言通行证。如您所见,ContextManager依赖从其他ContextManager获取更改通知并使用mergeChangesFromContextDidSaveNotification来更新自身。注意一切都发生在同一个线程上进行这个测试。

我知道NSManagedObjectContextDidSaveNotification正在发送和正确接收。我知道NSManagedObjectContextDidSaveNotification在其userInfo字典中有正确的数据。

我也运行这个单元测试作为应用程序测试在实际的设备上使用SQLite持久性存储 - 相同的断言失败。

在此先感谢!

ContextManager:

#import "ContextManager.h" 

@implementation ContextManager 

@synthesize context; 

#pragma mark - Custom code 
- (void)save { 

    NSError *error = nil; 
    if (self.context != nil) { 
     if ([self.context hasChanges] && ![self.context save:&error]) { 

      NSAssert1(FALSE, @"Unable to save the managed object context. UserInfo:\n%@", [error userInfo]); 

     } 
    } 

    return; 
} 

- (void)mergeChanges:(NSNotification *)notification { 

    if (notification.object != self.context) { 

     [self.context mergeChangesFromContextDidSaveNotification:notification]; 

    } 

    return; 
} 

#pragma mark - Overridden NSObject methods 
#pragma mark Creating, copying, and deallocating object 
- (id)initWithPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)persistentStoreCoordinator { 

    self = [super init]; 

    if (self) { 
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:nil]; 
     self.context = [[[NSManagedObjectContext alloc] init] autorelease]; 
     [self.context setPersistentStoreCoordinator:persistentStoreCoordinator]; 
    } 

    return self; 
} 

- (void)dealloc { 

    [context release]; 
    [super dealloc]; 
    return; 
} 

@end 

TestContextManager来:

#import "TestContextManager.h" 
#import "ContextManager.h" 
#import "CoreDataManager.h" 

#define TEST_MANAGED_OBJECT @"AManagedObject" 

@implementation TestContextManager 

- (void)testContextManager { 

    CoreDataManager *coreDataManager = [[CoreDataManager alloc] init]; 
    coreDataManager.storeType = NSInMemoryStoreType; 

    ContextManager *contextManagerA = [coreDataManager provideContextManager]; 
    if (!contextManagerA) STFail(@"CoreDataManager did not provide a context manager."); 

    NSManagedObjectContext *contextA = contextManagerA.context; 
    if (!contextA) STFail(@"ContextManager did not provide a managed object context."); 

    // setA1 has 0 objects (or whatever is initially there). 
    NSSet *setA1 = [contextManagerA.context registeredObjects]; 
    [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerA.context]; 

    // setA2 has 1 object. 
    NSSet *setA2 = [contextManagerA.context registeredObjects]; 
    STAssertTrue([setA2 count] == [setA1 count]+1, @"Context provided by ContextManager is not accepting new objects."); 
    [contextManagerA save]; 

    ContextManager *contextManagerB = [coreDataManager provideContextManager]; 
    [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerB.context];  
    [contextManagerB save]; 

    NSSet *setA3 = [contextManagerA.context registeredObjects]; 

    // setA3 should have 2 objects <=== THIS TEST FAILS 
    STAssertTrue([setA3 count] == [setA1 count]+2, @"Context is not updating new objects."); 

    [coreDataManager release];  


    return; 
} 

@end 

回答

1

你真的成立ContextManager观察用于保存managedObjectContext通知?你在这里没有表现出来,所以我只想覆盖最简单的情况。

对不起,我应该让这对埃里克的信息发表评论。

+0

是。这是在ContextManager的initWithPersistentStoreCoordinator:方法中完成的。我还使用日志记录来验证ContextManager正在发送和接收通知。 – Erik 2011-04-06 23:30:19

+1

哦,等一下,我刚刚注意到了一些东西。问题不在于测试中的合并。您对setA1&setA2的赋值导致指向相同的对象。您应该为每个对象创建一个注册对象的副本,或者在添加一个新对象之前获取每个对象的数量。 – 2011-04-06 23:38:21

+0

我认为NSManagedContext发送并自动释放NSSet作为registrationObjects的返回值。但是,以防万一,我已经更新了我的代码,下面我添加了。不幸的是,测试仍然失败...... – Erik 2011-04-06 23:53:14

1

感谢没有人知道回答我的问题。显然,我需要做一些阅读registeredObjects实际返回。我想这里的好消息是,实际的代码工作 - 单元测试是坏的......

这里的单元测试是正确行使被测单元并通过:

#import "TestContextManager.h" 
#import "ContextManager.h" 
#import "CoreDataManager.h" 

#define TEST_MANAGED_OBJECT @"AManagedObject" 

@implementation TestContextManager 

- (void)testContextManager { 

    CoreDataManager *coreDataManager = [[CoreDataManager alloc] init]; 
    coreDataManager.storeType = NSInMemoryStoreType; 

    ContextManager *contextManagerA = [coreDataManager provideContextManager]; 
    if (!contextManagerA) STFail(@"CoreDataManager did not provide a context manager."); 

    NSManagedObjectContext *contextA = contextManagerA.context; 
    if (!contextA) STFail(@"ContextManager did not provide a managed object context."); 

    NSEntityDescription *entityDescriptionA = [NSEntityDescription entityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextA]; 

    // make A1 request on an empty context (0 objects) 
    NSFetchRequest *requestA1 = [[NSFetchRequest alloc] init]; 
    [requestA1 setEntity:entityDescriptionA]; 
    NSError *errorA1 = nil; 
    NSArray *arrayA1 = [contextA executeFetchRequest:requestA1 error:&errorA1]; 
    if (arrayA1 == nil) STFail(@"Fetch request A1 failed."); 
    if ([arrayA1 count] != 0) STFail(@"Context A1 is not empty at start of test."); 

    // add an object to context A and make request A2 
    [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerA.context]; 
    NSFetchRequest *requestA2 = [[NSFetchRequest alloc] init]; 
    [requestA2 setEntity:entityDescriptionA]; 
    NSError *errorA2 = nil; 
    NSArray *arrayA2 = [contextA executeFetchRequest:requestA2 error:&errorA2]; 
    if (arrayA2 == nil) STFail(@"Fetch request A2 failed."); 
    if ([arrayA2 count] != 1) STFail(@"Context A2 did not successfully add an object."); 

    // add an object to context B and make request B1 
    ContextManager *contextManagerB = [coreDataManager provideContextManager]; 
    NSManagedObjectContext *contextB = contextManagerB.context; 
    NSEntityDescription *entityDescriptionB = [NSEntityDescription entityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextB]; 
    [NSEntityDescription insertNewObjectForEntityForName:TEST_MANAGED_OBJECT inManagedObjectContext:contextManagerB.context]; 
    NSFetchRequest *requestB1 = [[NSFetchRequest alloc] init]; 
    [requestB1 setEntity:entityDescriptionB]; 
    NSError *errorB1 = nil; 
    NSArray *arrayB1 = [contextB executeFetchRequest:requestB1 error:&errorB1]; 
    if (arrayB1 == nil) STFail(@"Fetch request B1 failed."); 
    if ([arrayB1 count] != 1) STFail(@"Context B1 did not successfully add an object."); 

    // save contextB 
    [contextManagerB save]; 

    // check if contextA was updated 
    NSFetchRequest *requestA3 = [[NSFetchRequest alloc] init]; 
    [requestA3 setEntity:entityDescriptionA]; 
    NSError *errorA3 = nil; 
    NSArray *arrayA3 = [contextA executeFetchRequest:requestA3 error:&errorA3]; 
    if (arrayA3 == nil) STFail(@"Fetch request A3 failed."); 
    if ([arrayA3 count] != 2) STFail(@"Context A did not update correctly."); 

    [requestA1 release]; 
    [requestA2 release]; 
    [requestB1 release]; 
    [requestA3 release]; 
    [coreDataManager release];  

    return; 
} 

@end