2011-04-06 3 views
1

J'écris un Core Data ContextManager pour une application iOS de plus grande taille. Un ContextManager fournit un NSManagedContext qui est automatiquement mis à jour lorsque d'autres ContextManagers enregistrent leur NSMangedContext dans le magasin de données persistantes.Données de base mergeChangesFromContextDidSaveNotification ne fonctionne pas

J'ai un test unitaire (TestContextManager) qui crée deux contextes, ajoute un objet à un et teste si l'objet apparaît dans l'autre contexte. Ce n'est pas le cas. Pourquoi le dernier test échoue-t-il?

Voici le code pour un ContextManager et le test d'unité défaillante. Le dernier assertion dans le test unitaire échoue. Tous les autres affirment passer. Comme vous pouvez le voir, le ContextManager repose sur l'obtention d'une notification de modification d'un autre ContextManager et l'utilisation de mergeChangesFromContextDidSaveNotification pour se mettre à jour. Notez que tout se passe sur le même thread pour ce test.

Je sais que NSManagedObjectContextDidSaveNotification est envoyé et reçu correctement. Je sais que NSManagedObjectContextDidSaveNotification a les données correctes dans son dictionnaire userInfo.

J'ai également exécuté ce test unitaire en tant que test d'application sur un périphérique réel à l'aide d'un magasin persistant SQLite - le même problème échoue.

Merci d'avance!

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 

Répondre

1

Est-ce que vous avez réellement mis en place le ContextManager d'observer la notification pour l'enregistrement d'une managedObjectContext? Vous ne montrez pas cela ici, donc je voulais juste couvrir le cas le plus simple.

Désolé, j'aurais dû faire un commentaire sur le post d'Erik.

+0

Oui. Ceci est fait dans la méthode initWithPersistentStoreCoordinator: de ContextManager. J'ai également utilisé la journalisation pour vérifier que les notifications sont envoyées et reçues par le ContextManager. – Erik

+1

Oh attends j'ai juste remarqué quelque chose. Le problème n'est pas la fusion, c'est dans le test. Votre affectation à setA1 & setA2 aboutit à pointer sur le même objet. Vous devez soit faire une copie des objets enregistrés pour chacun ou simplement obtenir le nombre de chacun avant d'ajouter un nouvel objet. –

+0

Je pense que NSManagedContext envoie et libère automatiquement NSSet en tant que valeur de retour à registeredObjects. Mais, au cas où, j'ai mis à jour mon code par mon ajout ci-dessous. Malheureusement, le test échoue toujours ... – Erik

1

Merci à peu connu pour répondre à ma question. De toute évidence, j'ai besoin de faire quelques lectures sur ce que registeredObjects retourne effectivement. Je suppose que les bonnes nouvelles ici est que le code actuel fonctionne - le test unitaire était mauvais ...

est ici le test unitaire qui exerce correctement l'unité testée et passe:

#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 
Questions connexes