OCMock 与 Core Data 动态属性问题

发布于 2024-08-13 15:29:52 字数 1754 浏览 4 评论 0原文

我正在使用 OCMock 来模拟一些核心数据对象。以前,我使用 Objective-C 1.0 风格的显式访问器实现了属性:

// -- Old Core Data object header
@interface MyItem : NSManagedObject {}
- (NSString *) PDFName;
- (void) setPDFName:(NSString *)pdfName;
@end

// -- implementation provides generated implementations for both getter and setter

现在我已将代码移至 Objective-C 2.0,并希望利用新的 @property 语法以及 Core Data 的动态生成方法实现对象:

// -- New Core Data object header
@interface MyItem : NSManagedObject {}
@property (nonatomic, retain) NSString *PDFName;
@end

// -- Core Data object implementation
@implementation MyItem
@dynamic PDFName;
@end

但是,现在当我创建一个模拟项时,它似乎无法处理动态属性:

// -- creating the mock item
id mockItem = [OCMockObject mockForClass:[MyItem class]];
[[[mockItem stub] andReturn:@"fakepath.pdf"] PDFName]; // <-- throws exception here

错误如下所示:

Test Case '-[MyItem_Test testMyItem]' started.
2009-12-09 11:47:39.044 MyApp[82120:903] NSExceptionHandler has recorded the following exception:
NSInvalidArgumentException -- *** -[NSProxy doesNotRecognizeSelector:PDFName] called!
Stack trace: 0x916a4d24 0x92115509 0x97879138 0x978790aa 0x9090cb09 0x97820db6 0x97820982 0x10d97ff 0x10d9834 0x9782005d 0x9781ffc8 0x20103d66 0x20103e8c 0x20103642 0x20107024 0x20103642 0x20107024 0x20103642 0x20105bfe 0x907fead9 0x977e4edb 0x977e2864 0x977e2691 0x90877ad9 0xbf565 0xbf154 0x107715 0x1076c3 0x1082e4 0x89d9b 0x8a1e5 0x894eb 0x907e81c7 0x978019a9 0x978013da 0x907dd094 0x907ea471 0x9478c7bd 0x9478c1b9 0x94784535 0x5ede 0x326a 0x5
Unknown.m:0: error: -[MyItem_Test testMyItem] : *** -[NSProxy doesNotRecognizeSelector:PDFName] called!

我做错了什么吗?是否有另一种方法可以使用 @dynamic 属性来模拟核心数据/对象?

I'm using OCMock to mock some Core Data objects. Previously, I had the properties implemented with Objective-C 1.0 style explicit accessors:

// -- Old Core Data object header
@interface MyItem : NSManagedObject {}
- (NSString *) PDFName;
- (void) setPDFName:(NSString *)pdfName;
@end

// -- implementation provides generated implementations for both getter and setter

Now I've moved the code to Objective-C 2.0 and want to take advantage of the new @property syntax, and the dynamically-generated method implementations for Core Data objects:

// -- New Core Data object header
@interface MyItem : NSManagedObject {}
@property (nonatomic, retain) NSString *PDFName;
@end

// -- Core Data object implementation
@implementation MyItem
@dynamic PDFName;
@end

However, now when I create a mock item, it doesn't seem to handle the dynamic properties:

// -- creating the mock item
id mockItem = [OCMockObject mockForClass:[MyItem class]];
[[[mockItem stub] andReturn:@"fakepath.pdf"] PDFName]; // <-- throws exception here

The error looks like this:

Test Case '-[MyItem_Test testMyItem]' started.
2009-12-09 11:47:39.044 MyApp[82120:903] NSExceptionHandler has recorded the following exception:
NSInvalidArgumentException -- *** -[NSProxy doesNotRecognizeSelector:PDFName] called!
Stack trace: 0x916a4d24 0x92115509 0x97879138 0x978790aa 0x9090cb09 0x97820db6 0x97820982 0x10d97ff 0x10d9834 0x9782005d 0x9781ffc8 0x20103d66 0x20103e8c 0x20103642 0x20107024 0x20103642 0x20107024 0x20103642 0x20105bfe 0x907fead9 0x977e4edb 0x977e2864 0x977e2691 0x90877ad9 0xbf565 0xbf154 0x107715 0x1076c3 0x1082e4 0x89d9b 0x8a1e5 0x894eb 0x907e81c7 0x978019a9 0x978013da 0x907dd094 0x907ea471 0x9478c7bd 0x9478c1b9 0x94784535 0x5ede 0x326a 0x5
Unknown.m:0: error: -[MyItem_Test testMyItem] : *** -[NSProxy doesNotRecognizeSelector:PDFName] called!

Am doing something wrong? Is there another way to mock a Core Data / object with @dynamic prooperties?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

绿光 2024-08-20 15:29:52

还回复了您在 OCMock 论坛

查看http://iamleeg.blogspot.com/2009/09/unit-testing-core-data-driven-apps.html

基本上,他建议将核心数据对象的接口抽象为协议,并使用该协议而不是传递核心数据对象实例的类。

我为我的核心数据对象执行此操作。然后你可以使用mockForProtocol:

id mockItem = [OCMockObject mockForProtocol:@protocol(MyItemInterface)];
[[[mockItem expect] andReturn:@"fakepath.pdf"] PDFName];

效果很好!他还建议创建一个接口的非核心数据模拟实现,它只是综合属性:

@implementation MockMyItem
@synthesize PDFName;
@end

...

id <MyItemInterface> myItemStub = [[MockMyItem alloc] init] autorelease];
[myItem setPDFName:@"fakepath.pdf"];

我也使用过这个,但我不确定它是否在mockForProtocol:/stub:方法上添加了任何内容,而且它又是一个要维护的东西。

Also responded to your cross-post on the OCMock Forum

Check out http://iamleeg.blogspot.com/2009/09/unit-testing-core-data-driven-apps.html.

Basically he suggests abstracting out your Core Data object's interface to a protocol, and using that protocol instead of the class where you pass instances of your core data object around.

I do this for my core data objects. Then you can use mockForProtocol:

id mockItem = [OCMockObject mockForProtocol:@protocol(MyItemInterface)];
[[[mockItem expect] andReturn:@"fakepath.pdf"] PDFName];

Works great! He also suggests creating a non-core data mock implementation of the interface which just synthesizes the properties:

@implementation MockMyItem
@synthesize PDFName;
@end

...

id <MyItemInterface> myItemStub = [[MockMyItem alloc] init] autorelease];
[myItem setPDFName:@"fakepath.pdf"];

I've used this as well, but I'm not sure it adds anything over the mockForProtocol:/stub: approach, and it's one more thing to maintain.

一身骄傲 2024-08-20 15:29:52

上面的答案并没有让我满意,因为我不喜欢为此创建一个协议。所以我发现有一种更简单的方法可以做到这一点。
而不是

[[[mockItem stub] andReturn:@"fakepath.pdf"] PDFName]; // <-- throws exception here

只写

[[[mockItem stub] andReturn:@"fakepath.pdf"] valueForKey:@"PDFName"];

The above answer didn't satisfy me, because I didn't like to create a protocol for that. So I found out that there is an easier way to do that.
Instead of

[[[mockItem stub] andReturn:@"fakepath.pdf"] PDFName]; // <-- throws exception here

Just write

[[[mockItem stub] andReturn:@"fakepath.pdf"] valueForKey:@"PDFName"];
歌入人心 2024-08-20 15:29:52

解决方案之一是使用协议,该协议旨在替代其原始接口,但它可能有点繁重,并导致您应该复制大量代码。

就我个人而言,我找到了一种使其轻量级的方法:

例如,在单元测试文件中,在单元测试类之前创建一个简单的类别:

@implementation MyItem(UnitTesing)
- (NSString *)PDFName{return nil;};
@end

此外,您可以将其保存在单独的文件中,但请确保该文件是不属于您的生产目标的一部分。这就是为什么我更喜欢将它保存在我想要使用它的同一个测试文件中。

此方法的巨大优点是您不应该复制 XCode 创建的用于支持关系的方法。您也可以仅将要在测试中调用的方法放入此类别。

不过,有一些注意事项,例如,当您要检查代码如何正确更改托管对象的属性时,您应该在类别内添加另一个方法来支持设置器:

- (void)setPDFName:(NSString *)name{};

One of solutions is using a protocol, which is intended to substitute it's original interface, but it could be a bit heavy and leads to significant amount of code you should duplicate.

Personally, I found a way to make it lightweight:

Create a simple category, for instance, inside your unit testing file, just before your unit testing class:

@implementation MyItem(UnitTesing)
- (NSString *)PDFName{return nil;};
@end

Also, you can keep it in separate file, but make sure, that this file is not a part of your production target. That is why I prefer to keep it in the same test file, where I want to use it.

The huge advantage of this method, is that you should not copy methods, that are created by XCode to support relationships. Also you can put to this category only methods you are going to call inside your tests.

There are some caveats, though, for example, you should add another methods inside the category, to support setters, when you are going to check, how correct your code changes the properties of your managed object:

- (void)setPDFName:(NSString *)name{};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文