XCTest和CoreData

时间:2014-01-30 15:21:28

标签: ios unit-testing core-data xctest

我正在尝试使用XCTest类和方法在Xcode 5中对我的模型进行单元测试。

因为我的模型类继承了managedObject,所以我不能只实例化(alloc / init)它们并调用getter和setter或我需要测试的方法。 我需要使用NSEntityDescription创建它们并使用managedObjectContext

就这一点而言,我遇到了麻烦。 我不知道在哪里以及如何为单元测试目的创建managedObjectContext

如果有人有一些建议或代码示例,那将非常有帮助。 感谢。

3 个答案:

答案 0 :(得分:12)

我使用内存存储来进行单元测试并在其中创建所有实体。

此类方法可以放在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;
}

这对我有用,因为我使用依赖注入来传递我的moc而不是使用单例。

答案 1 :(得分:12)

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

依赖注入

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

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

这是执行"依赖注入"的最低要求。你不需要一个花哨的框架来为你做注射。相反,在您的应用程序中,您可以分配通常使用SQLite存储的NSManagedObjectContext实例。在测试中,您使用内存存储创建单独的NSManagedObjectContext并将其传递给SomeController。这甚至适用于使用OCMock的(部分)模拟。

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

如何访问MOC

查看objc.io example code at GitHub,您将看到一个PersistentStack帮助程序类,它负责初始化应用程序的托管对象上下文。 Chris使用抽象测试用例子类来提供testing context

总体方针是:

  1. 在整个代码中不要依赖单身辅助程序类,因为很难用测试上下文替换其上下文。这是我发现的特殊性,这是由于XCTest被注入"进入运行的应用程序代码。网上有一些工作示例,但它们没有达到我对Xcode 5.1和XCTest所期望的效果。
  2. 相反,请在适当的时候准备一次NSManagedObjectContext。传递托管对象并使用托管对象来访问上下文。
  3. Florian Kugler states it this way:

      

    托管对象应该在应用程序中传递,至少跨越模型 - 控制器障碍,甚至可能跨越控制器视图障碍。后者在某种程度上更具争议性,并且可以通过例如更好的方式进行抽象。定义一个对象必须符合的协议才能被某个视图使用,或者通过在视图类别中实现配置方法来弥合从模型对象到视图细节的差距。

         

    无论如何,我们不应该将托管对象限制在模型层,并且只要我们想要传递它们就将它们的数据拉出到不同的结构中。托管对象是Core Data应用程序中的一等公民,我们应该相应地使用它们。例如,应在视图控制器之间传递托管对象,以便为它们提供所需的数据。

         

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

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

    如果您已经将模型对象传递给视图控制器,那么直接通过此对象访问上下文要好得多:

    NSManagedObjectContext *context = self.myObject.managedObjectContext;
    
         

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

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

答案 2 :(得分:1)

夫特

NSManagedObjectContext扩展,为测试创建内存中的CoreData堆栈

extension NSManagedObjectContext {

    class func contextForTests() -> NSManagedObjectContext {
        // Get the model
        let model = NSManagedObjectModel.mergedModel(from: Bundle.allBundles)!

        // Create and configure the coordinator
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
        try! coordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)

        // Setup the context
        let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        context.persistentStoreCoordinator = coordinator
        return context
    }

}

然后在测试中使用它:

class YourTests: XCTestCase {

    private var context: NSManagedObjectContext?

    override func setUp() {
        self.context = NSManagedObjectContext.contextForTests()
    }

    // And use it in your tests
}