XCTest和CoreData

我想通过使用XCTest类和方法在Xcode 5中unit testing我的模型。

因为我的模型类inheritance了managedObject ,所以我不能只实例化(alloc / init)它们并调用getter和setter或者我需要testing的方法。 我需要通过使用NSEntityDescription创build它们并使用managedObjectContext

这就是我得到麻烦的一点。 我不知道在哪里以及如何为unit testing创​​buildmanagedObjectContext目的。

如果有人有一些build议或代码的例子,这将是非常有益的。 谢谢。

我使用内存存储来进行unit testing,并创build所有的实体。

这个类方法可以放在TestsHelper.m

 + (NSManagedObjectContext *)managedObjectContextForTests { static NSManagedObjectModel *model = nil; if (!model) { model = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]]; } NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; NSPersistentStore *store = [psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:nil]; NSAssert(store, @"Should have a store by now"); NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; moc.persistentStoreCoordinator = psc; return moc; } 

这适用于我,因为我使用dependency injection来传递我的moc而不是使用单例。

我同意@Abizern:将实例传递给代码中的NSManagedObjectContext ,而不是依赖于您的应用程序委托,全局variables或自定义帮助器单例。

dependency injection

如果您知道某个控制器需要访问,请在其init方法中添加一个NSManagedObjectContext参数,并对其进行强有力的引用:

 @interface SomeController : NSObject @property (nonatomic, strong, readwrite) NSManagedObjectContext *context; - (instancetype)initWithContext:(NSManagedObjectContext *)context; @end 

这是做“dependency injection”的最低要求。 你不需要一个花哨的框架来为你做注射。 相反,在你的应用程序中,你可以指定一个通常使用SQLite存储的NSManagedObjectContext实例。 在你的testing中,你创build一个单独的带有内存存储的NSManagedObjectContext并把它传递给SomeController 。 这甚至可以与使用OCMock的(部分)模拟工作。

在objc.io#4上有一些很好的例子,尤其是Chris Eidhof的示例应用程序: http ://www.objc.io/issue-4/full-core-data-application.html

如何访问MOC

查看GitHub上的objc.io示例代码 ,您将看到一个PersistentStack帮助器类,它负责为应用程序初始化托pipe对象上下文。 Chris使用抽象testing用例子类来提供testing上下文 。

总的指导方针是这样的:

  1. 不要在整个代码中依赖单例助手类,因为很难用testing上下文replace它的上下文。 这是我发现的一个特点,这是由于XCTest被“注入”到正在运行的应用程序代码中。 在networking上有一些工作的例子,但是他们没有按照我对Xcode 5.1和XCTest的预期。
  2. 相反,在适当的时候准备一个NSManagedObjectContext 。 传递托pipe对象并使用托pipe对象访问上下文。

弗洛里安·库格(Florian Kugler) 这样说:

pipe理对象应该在应用程序中传递,至less跨越模型控制器的障碍,甚至可能是控​​制器视图的障碍。 后者虽然有点争议,但可以通过定义一个对象必须符合的协议以便被某个视图使用的协议,或者通过在视图类中实现configuration方法来弥补差距从模型对象到视图的细节。

无论如何,我们不应该将pipe理对象限制在模型层,只要我们想传递它们,就把它们的数据抽出到不同的结构中。 pipe理对象是核心数据应用程序中的一等公民,我们应该相应地使用它们。 例如,pipe理对象应该在视图控制器之间传递,以向它们提供他们需要的数据。

为了访问被pipe理的对象上下文,我们经常在视图控制器中看到这样的代码:

 NSManagedObjectContext *context = [(MyApplicationDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; 

如果您已经将模型对象传递给视图控制器,则通过此对象直接访问上下文会更好:

 NSManagedObjectContext *context = self.myObject.managedObjectContext; 

这消除了对应用程序委托的隐藏的依赖关系,并使其更易读,也更容易testing。

这是我得到的最好的整体build议。 现在,我可以在需要的地方testing核心数据使用情况。 以前,通过全局单例类(自定义类或应用程序委托)访问上下文很方便在生产中使用,但在testing中很难validation。