单元测试模型继承自NSManagedObject的类

时间:2010-05-16 01:05:26

标签: objective-c unit-testing core-data

所以......我正试图在我的iPhone应用程序中设置单元测试,但我遇到了一些问题。我正在尝试测试我的模型类,但它们直接从NSManagedObject继承。我确定这是一个问题,但我不知道如何绕过它。

所有内容都按预期构建和运行但在调用我正在测试的类上的任何方法时出现此错误:

Unknown.m:0:0 unrecognized selector sent to instance 0xc2b120

如果我按照this structure在我的测试中创建我的对象,我最终会出现另一个错误,但它仍然无法帮助我。

如果我像这样实例化我的模型:

entry = [[TimeEntry alloc]
        initWithEntity:nil
        insertIntoManagedObjectContext:nil];

然后我在运行时结束这个错误:

An NSManagedObject of class 'TimeEntry' must have a valid NSEntityDescription.

如果我这样试试:

entry = [[TimeEntry alloc] init];

然后我最终得到了这个错误:

unrecognized selector sent to instance 0xc2b120

如果我遵循模式laid out here

model = [[NSManagedObjectModel mergedModelFromBundles: nil] retain];
NSLog(@"model: %@", model);
coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model];
store = [coord addPersistentStoreWithType: NSInMemoryStoreType
                            configuration: nil
                                      URL: nil
                                  options: nil 
                                    error: NULL];
ctx = [[NSManagedObjectContext alloc] init];
[ctx setPersistentStoreCoordinator: coord];

entry = (TimeEntry *)[NSEntityDescription insertNewObjectForEntityForName:@"TimeEntry" inManagedObjectContext:ctx];

然后我收到此错误:

could not locate an entity named 'TimeEntry' in this model.

基本上我的问题是:如何测试从NSManagedObject继承的类?

3 个答案:

答案 0 :(得分:7)

为了实例化NSManagedObject,您需要一个实体。因此,您首次尝试的方法 - 为实体传递nil或使用裸-init(NSManagedObject不支持) - 都不起作用。通过使用-[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:]创建对象,您正在做正确的事情,您只需要:

  1. 确保数据模型中存在实体TimeEntry。
  2. 确保实体 TimeEntry与数据模型中的 TimeEntry相关联。
  3. 确保您的测试实际加载了您的数据模型。
  4. 请注意,除非您特别想要测试保存/删除验证,否则通常不需要向协调器添加持久性存储。 (如果您在应用程序中使用SQLite持久性存储,我强烈建议您在测试中使用一个;不同的持久性存储类型具有不同的性能特征和支持的查询。)

    为了确保您的数据模型已加载,您会发现实际指定加载它的URL会更有成效,而不是仅仅希望您将它放在正确的位置并且{{1会做正确的事情。我将它作为单元测试包目标的成员,因此它被编译到您的单元测试包的资源中。这样你就可以使用适当的NSBundle方法来获取它的路径或URL。

    最后,您将要在测试用例的-mergedModelFromBundles:方法中设置核心数据持久性堆栈 - 模型,持久性存储协调器和暂存上下文。或者在测试用例基类的-setUp方法中,如果要创建多个测试用例类。 (当然,对于持久性堆栈和-setUp方法的拆除也是如此。)

答案 1 :(得分:4)

我在github上为Core Data Test环境创建了一个Sample http://github.com/mbrugger/CoreDataDependentProperties/blob/master/LPAutomatedObserving/Tests/ManagedObjectSenTestCase.m

从ManagedObjectSenTestCase.m / h继承您的测试用例,并使用您的测试目标包标识符和数据模型名称调整以下两行

        NSBundle* bundle = [NSBundle bundleWithIdentifier:@"com.yourcompany.ModelTest"];

    NSString* path = [bundle pathForResource:@"DataModel" ofType:@"mom"];

代码示例:

-(void) setUp
{
    pool = [[NSAutoreleasePool alloc] init];

    NSMutableSet *allBundles = [[[NSMutableSet alloc] init] autorelease];
    [allBundles addObjectsFromArray:[NSBundle allBundles]];

    NSBundle* bundle = [NSBundle bundleWithIdentifier:@"com.yourcompany.ModelTest"];

    NSString* path = [bundle pathForResource:@"DataModel"
                                                                                            ofType:@"mom"];

    NSURL* modelURL = [NSURL URLWithString:path];
    self.model = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] autorelease];

    self.coordinator = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model] autorelease];


    LPManagedObjectContext* tempContext = [[[NSManagedObjectContext alloc] init] autorelease];


    [tempContext setPersistentStoreCoordinator:coordinator];
    [tempContext setRetainsRegisteredObjects:YES];

    self.context = tempContext;
}

    -(void) tearDown
{
    NSLog(@"BEGIN: ManagedObjectSenTestCase tearDown");
    @try
    {
        self.context= nil;
        self.model = nil;
        self.coordinator = nil;
        [pool release];
        pool = nil;
    }
    @catch (NSException * e)
    {
        NSLog(@"%@",e);
        NSLog(@"%@", [e callStackSymbols]);
        NSLog(@"context reset failed!");
        @throw(e);

    }
    NSLog(@"END: ManagedObjectSenTestCase tearDown");
}

此示例创建核心数据堆栈,您可以将实体插入到创建的上下文中进行测试。

答案 2 :(得分:0)

我遇到了同样的问题。我最终认为它无法检索我的模型,但作为iPhone开发的新手,我无法解决如何根据Chris的建议从URL加载它。

从运行测试的包中加载它对我有用:

@implementation WhenWorkingWithATiming

Timing *timing;

NSManagedObjectModel *model;
NSPersistentStoreCoordinator *coordinator;
NSManagedObjectContext *context;


- (void) setUp {
    NSArray *bundles = [NSArray arrayWithObject:[NSBundle bundleForClass:[self class]]];
    model = [[NSManagedObjectModel mergedModelFromBundles:bundles] retain];
    NSLog(@"Model: %@", model);

    coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:coordinator];

    timing = (Timing *)[NSEntityDescription insertNewObjectForEntityForName:@"Timing" inManagedObjectContext:context];
}

- (void) tearDown {
    [context rollback];
    [context release];
    [coordinator release];
    [model release];
}

- (void) testThatTimingIsInitialised {
    STAssertNotNil(timing, @"should have a timing");
}

@end