Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 测试核心数据应用程序_Objective C_Unit Testing_Core Data_Ocmock_Cedar Bdd - Fatal编程技术网

Objective c 测试核心数据应用程序

Objective c 测试核心数据应用程序,objective-c,unit-testing,core-data,ocmock,cedar-bdd,Objective C,Unit Testing,Core Data,Ocmock,Cedar Bdd,我应该如何测试测试 起初,我想到了。而且,在我搜索该文档时,我偶然发现并认为也许我不应该创建我创建的方法,而是应该创建获取请求模板,但它看起来不像entityName可以随获取请求模板而变化,是吗?我是否可以在NSManagedObject上创建一个获取请求模板,以便所有子类都可以使用它?嗯,但是我仍然需要一个entityName,而且我认为没有办法动态获取调用该方法的子类的名称 无论如何,看起来一个好的解决方案是,独立于生产核心数据堆栈。这似乎类似于Rails有一个单独的数据库进行测试。但是

我应该如何测试测试

起初,我想到了。而且,在我搜索该文档时,我偶然发现并认为也许我不应该创建我创建的方法,而是应该创建获取请求模板,但它看起来不像
entityName
可以随获取请求模板而变化,是吗?我是否可以在
NSManagedObject
上创建一个获取请求模板,以便所有子类都可以使用它?嗯,但是我仍然需要一个
entityName
,而且我认为没有办法动态获取调用该方法的子类的名称

无论如何,看起来一个好的解决方案是,独立于生产核心数据堆栈。这似乎类似于Rails有一个单独的数据库进行测试。但是

你认为哪种方法更好?我个人更喜欢后者

更新:我很高兴。除了编写下面的代码外,我还将
NSManagedObject+Additions.m
User.m
添加到
Spec
目标中

#define HC_SHORTHAND
#import <Cedar-iPhone/SpecHelper.h>
#import <OCHamcrestIOS/OCHamcrestIOS.h>

#import "NSManagedObject+Additions.h"
#import "User.h"

SPEC_BEGIN(NSManagedObjectAdditionsSpec)

describe(@"NSManagedObject+Additions", ^{
    __block NSManagedObjectContext *managedObjectContext;   

    beforeEach(^{
        NSManagedObjectModel *managedObjectModel =
                [NSManagedObjectModel mergedModelFromBundles:nil];

        NSPersistentStoreCoordinator *persistentStoreCoordinator =
                [[NSPersistentStoreCoordinator alloc]
                 initWithManagedObjectModel:managedObjectModel];

        [persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType
                                                 configuration:nil URL:nil options:nil error:NULL];

        managedObjectContext = [[NSManagedObjectContext alloc] init];
        managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator;

        [persistentStoreCoordinator release];
    });

    it(@"finds first object by attribute value", ^{

        // Create a user with an arbitrary Facebook user ID.
        NSNumber *fbId = [[NSNumber alloc] initWithInteger:514417];
        [[NSEntityDescription insertNewObjectForEntityForName:@"User"
                                      inManagedObjectContext:managedObjectContext] setFbId:fbId];
        [managedObjectContext save:nil];

        NSNumber *fbIdFound = [(User *)[User findByAttribute:@"fbId" value:(id)fbId
                                                  entityName:@"User"
                                      inManagedObjectContext:managedObjectContext] fbId];

        assertThatInteger([fbId integerValue], equalToInteger([fbIdFound integerValue]));

        [fbId release];
    });

    afterEach(^{
        [managedObjectContext release];
    }); 
});

SPEC_END

然后您将获得额外积分!:)如果参数应该是
id
,我似乎不应该将
NSNumber
转换为
id
,因为
NSNumber
id
,是吗?

我的个人哲学是,如果测试不测试真实事物,那么测试就不是测试,所以我对任何孤立测试片段的方法都不屑一顾。尽管它在许多情况下都能工作,特别是在过程代码中,但在复杂的代码中,例如在核心数据对象图中,它可能会失败

核心数据中的大多数故障点都来自于错误的数据模型,例如,缺少一个相互关系,因此图形失去平衡,并且存在孤立对象。测试坏图的唯一方法是创建一个已知的图,然后对代码进行压力测试,看看它是否能够找到并操作图中的对象

要实施这种类型的测试,我将执行以下操作:

  • 通过删除以前存在的核心数据存储文件开始每个测试运行,以便存储始终以已知状态开始
  • 为每次运行提供一个新的存储,最好每次都以代码形式生成,但您可以在每次运行之前交换存储文件的副本。我更喜欢前一种方法,因为从长远来看它实际上更容易
  • 确保测试数据包含相对极端的示例,例如长名称、带有垃圾字符的字符串、非常大和非常小的数字等
  • 在每次测试时,测试对象图的状态应该是绝对已知的。我经常在测试中转储整个图形,并且我有详细转储实体和活动对象的方法

    我通常在一个单独的应用程序项目设置中开发和测试应用程序的整个数据模型,除了开发数据模型之外什么都不做。只有当我让数据模型完全按照应用程序中的需要工作时,我才会将其移动到整个项目中,并开始添加控制器和接口

    由于数据模型是正确实现的模型视图控制器设计应用程序的实际核心,因此获得正确的数据模型涵盖了开发的50%-75%。剩下的是蛋糕散步

    在这种特殊情况下,您实际上只需要测试fetch请求的谓词是否返回正确的对象。唯一的测试方法是提供完整的测试图

    (我要指出的是,这种方法在实践中是非常无用的。它不会按属性返回任何特定对象,而只是返回任意数量的具有该值属性的对象中的任何一个。例如,如果您有一个对象图,其中23462
    Person
    对象的
    firstName
    属性值为
    John
    ,此方法将返回23462中的任意一个人实体。我看不出这一点。我认为您是在用过程SQL术语思考问题。这将导致在处理对象图管理器(如核心数据)时出现混乱。)

    更新:

    我猜您的错误是由编译器查看谓词中
    值的用法并假设它必须是NSString对象引起的。当您以字符串格式删除对象时,如
    谓词使用的字符串格式:
    ,返回的实际值是包含
    des结果的NSString对象对象的scription
    方法。因此,对编译器而言,谓词实际上如下所示:

    [NSPredicate predicateWithFormat:@"%K == %@", (NSString *)attribute, (NSString *)value]
    
    …因此,当它向后工作时,它将在
    value
    参数中查找NSString,尽管从技术上讲它不应该这样做。这种id的使用实际上不是最佳做法,因为它将接受任何类,但实际上您并不总是知道实例的
    -description
    方法返回的描述字符串是什么

    正如我上面所说,你在这里有一些概念上的问题。当你在下面的评论中说:

    我的目的是要制定一个方法 类似于ActiveRecord的find_by_ 动态查找器

    …您从错误的角度处理核心数据。Active Record主要是围绕SQL的对象包装器,使现有SQL Server与Ruby on Rails更容易集成。因此,它主要由过程SQL概念控制

    这与核心数据所使用的方法完全相反。核心数据首先是一个对象图管理系统,用于创建模型视图控制器应用程序设计的模型层。因此,对象就是一切。例如,甚至可能有没有属性的对象,只有关系。这些对象可能有非常复杂的beh这在SQL甚至Active record中都是不存在的

    很可能有任意数量的对象具有完全相同的属性。这使得您试图创建的方法毫无价值和危险,因为您永远不知道将返回哪个对象。这使得它成为“混乱”方法。如果您有
    [NSPredicate predicateWithFormat:@"%K == %@", (NSString *)attribute, (NSString *)value]