19

OCMock を使用して、一部の Core Data オブジェクトをモックしています。以前は、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 プロパティを使用してコア データ/オブジェクトをモックする別の方法はありますか?

4

3 に答える 3

21

また、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: アプローチに何かを追加するかどうかはわかりません。これは、維持する必要があるもう 1 つのことです。

于 2010-05-07T18:30:05.343 に答える
11

そのためのプロトコルを作成するのが好きではなかったので、上記の答えは私を満足させませんでした。そこで、もっと簡単にできる方法があることを知りました。それ以外の

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

書くだけ

[[[mockItem stub] andReturn:@"fakepath.pdf"] valueForKey:@"PDFName"];
于 2013-11-07T08:50:58.320 に答える
3

解決策の 1 つは、プロトコルを使用することです。これは、元のインターフェイスを置き換えることを目的としていますが、少し重くなり、複製する必要のあるコードが大量になる可能性があります。

個人的には、軽量化する方法を見つけました:

たとえば、単体テスト クラスの直前に、単体テスト ファイル内に単純なカテゴリを作成します。

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

また、別のファイルに保存することもできますが、このファイルが本番ターゲットの一部ではないことを確認してください。そのため、使用したい場所と同じテストファイルに保存することを好みます。

このメソッドの大きな利点は、関係をサポートするために XCode によって作成されたメソッドをコピーしないことです。また、テスト内で呼び出すメソッドのみをこのカテゴリに入れることもできます。

ただし、いくつかの注意点があります。たとえば、セッターをサポートするために、カテゴリ内に別のメソッドを追加して、コードが管理対象オブジェクトのプロパティをどのように変更するかを確認する必要があります。

- (void)setPDFName:(NSString *)name{};
于 2013-11-23T20:51:44.053 に答える