Objective c 在运行iOS应用程序测试时,如何切换到内存存储?

Objective c 在运行iOS应用程序测试时,如何切换到内存存储?,objective-c,ios,unit-testing,core-data,Objective C,Ios,Unit Testing,Core Data,对于核心数据对象,我有一个相当标准的Xcode生成接口,即我的应用程序委托上的以下属性: @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext; @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel; @property (readonly, strong, nonatomi

对于核心数据对象,我有一个相当标准的Xcode生成接口,即我的应用程序委托上的以下属性:

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
现在我正在编写应用程序测试,但我想使用内存中的核心数据数据库,该数据库在每次测试运行时都会重置。我已经想出了一个办法,但感觉很奇怪:

  • 我在app delegate类中有一个静态变量,
    storeType
  • -persistentstorecordinator
    将其设置为
    NSSQLiteStoreType
    ,如果它是
    nil
    。这将是默认值,并且在生产中是唯一的值,以确保在运行应用程序时一切正常
  • 我确保为所有调试版本(包括我的应用程序测试目标)设置了
    DEBUG
  • 如果设置了
    DEBUG
    ,请在应用程序委托中定义一个方法,
    -resetCoreData
    。方法如下所示:

    #ifdef DEBUG
    - (void)resetCoreData {
        // Testing, we want to use the in memory store.
        storeType = NSInMemoryStoreType;
    
        // Disconnect core data.
        __persistentStoreCoordinator = nil;
        __managedObjectContext = nil;
    
        // Set up defaults.
        [self configureCoreDataDefaults];
    }
    #endif
    
    @interface CollectionsDAO : NSObject
    
    @property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
    @property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
    @property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
    
    + (CollectionsDAO *)defaultDAO;
    
    @end
    
    请注意,它将静态变量
    storeType
    设置为
    nsimemorystoretype
    -configureCoreDataDefaults
    方法创建一些应该始终存在的托管对象

  • 在我的应用程序测试基类中,我有
    -setup
    调用
    -resetCoreData

    - (void)setUp {
        [super setUp];
        [[[UIApplication sharedApplication] delegate] resetCoreData];
    }
    
这给了我我想要的:一个新的核心数据存储,为每个测试方法创建默认对象

但这很烦人。我已经将测试环境的知识添加到我的应用程序委托中,以使其在运行应用程序测试时表现不同。真恶心


那么,有什么更好的方法可以做到这一点呢?如何操作?

我建议创建DAO或类似工具来隔离核心数据设置。然后,使用一个类别,您可以在您的测试目标中定义并使用这个“resetCoreData”。

是@eduardo costa的答案的后续,我已经接受了这个答案,并使用了我用来使它工作的代码

首先,我创建了一个DAO类,并将所有核心数据属性都移到了那里。.h文件如下所示:

#ifdef DEBUG
- (void)resetCoreData {
    // Testing, we want to use the in memory store.
    storeType = NSInMemoryStoreType;

    // Disconnect core data.
    __persistentStoreCoordinator = nil;
    __managedObjectContext = nil;

    // Set up defaults.
    [self configureCoreDataDefaults];
}
#endif
@interface CollectionsDAO : NSObject

@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

+ (CollectionsDAO *)defaultDAO;

@end
现在我只要在需要访问核心数据的地方使用这个类
-defaultDAO
返回该类的静态实例,因此我可以在任何地方使用一个实例。您没有看到的是一个私有实例方法,
-storeType
,它返回
NSSQLiteStoreType
。这用于创建存储。我会在下面再谈

接下来,我在这个类上创建了一个类别,用于测试。头文件:

#import "CollectionsDAO.h"

@interface CollectionsDAO (Test)

+ (void)setupTestDAO;
+ (void)clearData;

@end
以及实施:

#import "CollectionsDAO+Test.h"
#include <objc/runtime.h>

static CollectionsDAO *testDAO;
@implementation CollectionsDAO (Testing)

+ (CollectionsDAO *)testDAO {
    if (testDAO == nil) testDAO = [[self alloc] init];
    return testDAO;
}

+ (void)setupTestDAO {
    method_setImplementation(
        class_getClassMethod(self.class, @selector(defaultDAO)),
        method_getImplementation(class_getClassMethod(self.class, @selector(testDAO)))
    );
}

+ (void)clearData {
    testDAO = nil;
}

- (NSString *)storeType {
    return NSInMemoryStoreType;
}

@end
现在测试总是使用内存来存储核心数据,DAO的测试实例总是由
+defaultDAO
返回(因为
+setupTestDAO
将其转换到位),并且每次测试后都会清除核心数据


我觉得这比以前干净多了。我花了一段时间才弄明白,但爱德华多的答案是正确的,我只需要花一段时间来弄清楚细节。

你能假设核心数据正在工作,并使用模拟存储进行测试吗?您真的需要测试核心数据吗?通过使用内存模型,我已经达到了与模拟核心数据相同的效果:每次调用后,数据都会在内存中结束,因此我可以检查它是否正确。实际上,模拟核心数据需要做更多的工作。使用类别是个好主意(为什么我总是忘记它们??),但我不知道DAO(我想是数据访问对象)是什么意思。你能详细说明一下吗?是的,数据访问对象。它只是一个具有CoreData逻辑的对象。我只是喜欢向DAO添加一个类别,而不是向我的主代理添加一个类别。另外,当我这样做时,我的代理往往会收缩很多(旧的“将应用程序分层”的事情:)。因此,问题是
-resetCoreData
主要将一些私有IVAR设置为
nil
。我看不出一个简单的分类方法。为了安全使用,我希望属性保持只读。不过,我还需要再考虑一下。好的,我接受了,但我已经添加了我自己的答案,并详细说明了我是如何使它真正起作用的。谢谢你的指点!