gpt4 book ai didi

objective-c - 测试核心数据应用

转载 作者:行者123 更新时间:2023-12-04 20:22:06 25 4
gpt4 key购买 nike

我应该如何测试 findByAttribute instance method I added to NSManagedObject ?

一开始我想到了programmatically creating an independent Core Data stack as demonstrated by Xcode's Core Data Utility Tutorial .而且,在我搜索该文档时,我遇到了 Core Data Fetch Request Templates并认为也许不是创建我创建的方法,我应该创建获取请求模板,但它看起来不像 entityName可以通过 fetch 请求模板可变,可以吗?我可以在 NSManagedObject 上创建获取请求模板吗?以便所有子类都可以使用它?嗯,但是我仍然需要一个 entityName而且我认为没有办法动态获取调用该方法的子类的名称。

无论如何,看起来不错的解决方案是create an in-memory Core Data stack for testing ,独立于生产核心数据堆栈。 @Jeff Schilling also recommends creating an in-memory persistent store . Chris Hanson also creates a persistent store coordinator to unit test Core Data .这似乎类似于 Rails 有一个单独的数据库进行测试。但是,@iamleeg recommends removing the Core Data dependence .

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

更新:我是 unit testing Core Data with OCHamcrest and Pivotal Lab's Cedar .除了写下面的代码,我还加了NSManagedObject+Additions.mUser.mSpec目标。

#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

如果你能告诉我为什么如果我不这样做 cast to (id) the fbId argument passed to findByAttribute 我得到
warning: incompatible Objective-C types 'struct NSNumber *',
expected 'struct NSString *' when passing argument 2 of
'findByAttribute:value:entityName:inManagedObjectContext:' from
distinct Objective-C type

然后你得到奖励积分! :) 看来我不应该投 NSNumberid如果参数应该是 id因为 NSNumberid , 对?

最佳答案

我个人的理念是,如果测试不测试真实的东西,那么它就不是测试,所以我对任何单独测试片段的方法都持怀疑态度。尽管它在很多情况下都可以工作,尤其是在过程代码中,但在复杂代码中很可能会失败,例如在 Core Data 对象图中发现的代码。

Core Data 中的大多数故障点来自错误的数据模型,例如缺少互惠关系,因此图形失去平衡,并且您有孤立的对象。测试错误图形的唯一方法是创建一个已知图形,然后对您的代码进行压力测试,看看它是否可以找到并操作图形中的对象。

为了实现这种类型的测试,我执行以下操作:

  • 通过删除先前存在的 Core Data 存储文件来启动每个测试运行,以便存储始终以已知状态开始。
  • 为每次运行提供一个新存储,最好每次都在代码中生成它,但您可以在每次运行之前交换存储文件的副本。我更喜欢前一种方法,因为从长远来看它实际上更容易。
  • 确保测试数据包含相对极端的例子,例如长名称、带有垃圾字符的字符串、非常大和非常小的数字等。

  • 测试对象图的状态在每次测试时应该是绝对知道的。我经常在测试中转储整个图,并且我有方法详细转储实体和事件对象。

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

    由于数据模型是正确实现的模型- View - Controller 设计应用程序的实际核心,因此正确的数据模型涵盖了 %50-%75 的开发过程。剩下的就是走蛋糕了。

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

    (我会注意到这个方法在实践中真的非常无用。它不会按属性返回任何特定对象,而只会返回具有该值属性的任意数量的对象中的任何一个。例如,如果你有一个对象图23,462 Person 具有 firstName 属性值为 John 的对象,此方法将返回 23,462 中的任意 Person 实体。我看不出这有什么意义。我想您是在程序 SQL 术语中思考的。在处理像 Core Data 这样的对象图管理器时会导致混淆。)

    更新:

    我猜你的错误是由编译器查看 value 的使用引起的。在谓词中并假设它必须是一个 NSString 对象。当您以字符串格式删除对象时,就像 predicateWithFormat: 使用的那样,实际返回的值是一个 NSString 对象,其中包含 description 的结果。对象的方法。所以,对编译器来说,你的谓词实际上是这样的:
    [NSPredicate predicateWithFormat:@"%K == %@", (NSString *)attribute, (NSString *)value]

    ...所以当它向后工作时,它将在 value 中寻找一个 NSString参数,即使从技术上讲它不应该。这种使用 id 确实不是最佳实践,因为它会接受任何类,但实际上您并不总是知道实例的 -description 返回的描述字符串是什么。方法会。

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

    My intention was to make a method analogous to ActiveRecord's find_by_ dynamic finder.



    ...您从错误的角度接近核心数据。 Active Record 主要是 SQL 的对象包装器,可以更轻松地将现有 SQL 服务器与 Ruby on Rails 集成。因此,它由过程 SQL 概念主导。

    这与 Core Data 使用的方法完全相反。 Core Data 首先是一个对象图管理系统,用于创建模型- View - Controller 应用程序设计的模型层。因此,对象就是一切。例如。甚至可以拥有没有属性、只有关系的对象。此类对象也可能具有非常复杂的行为。这在 SQL 甚至事件记录中确实不存在。

    很可能有任意数量的对象具有完全相同的属性。这使得您尝试创建的方法毫无值(value)且危险,因为您永远不知道将返回哪个对象。这使它成为一种“困惑”的方法。如果您有多个具有相同属性的对象,该方法将任意返回与提供的属性值匹配的任何单个对象。

    如果要识别特定对象,则需要捕获该对象的 ManagedObjectID然后使用 -[NSManagedObjectContext objectForID:]取回它。保存对象后,其 ManagedObjectID是独特的。

    但是,该功能通常仅在您必须引用不同商店甚至不同应用程序中的对象时使用。否则通常没有意义。在使用 Core Data 时,您不仅要根据对象的属性,还要根据对象的位置来查找对象,即它们在对象图中与其他对象的关系。

    让我复制并粘贴一些非常重要的建议:Core Data 不是 SQL。实体不是表格。对象不是行。列不是属性。 Core Data 是一个对象图管理系统,它可能会或可能不会持久化对象图,并且可能会或可能不会在幕后使用 SQL 来实现。试图用 SQL 术语来考虑 Core Data 会导致你完全误解 Core Data 并导致很多痛苦和浪费时间。

    尝试使用您已经熟悉的 API 的设计来编写新 API 是很自然的,但是当新 API 与旧 API 具有截然不同的设计理念时,这是一个危险的陷阱。

    如果您发现自己试图在新 API 中编写旧 API 的基本功能,仅此一项就应该警告您,您与新 API 理念不同步。在这种情况下,您应该问为什么如果通用 findByAttribute方法在 Core Data 中很有用,为什么 Apple 不提供?你是不是更有可能错过了 Core Data 中的一个重要概念?

    关于objective-c - 测试核心数据应用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5347720/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com