1

単体テストで OCMock を使用して NSManagedObjects をモックしています。私は Mogenerator を使用して、コア データ オブジェクトの機械および人間が読み取り可能なファイルを生成します。ブール値と文字列を返すために NSManagedObject をモックしようとしています。どちらもコア データ エンティティの属性です。をモックするBOOLと、値がオブジェクトから正しく返され、クラスがそれを使用し、テストが正常にパスします。NSString同じオブジェクトのプロパティをスタブしようとすると、 NSInvalidArgumentException [NSProxy doesNotRecognizeSelector].

呼び出しコードは次のとおりです。

id biller = [OCMockObject mockForClass:[Biller class]];
// passes
[[[biller stub] andReturnValue:OCMOCK_VALUE((BOOL){NO})] billerValidatedValue];
// throws exception
[[[biller stub] andReturn:@"Test"] name];

例外は次のとおりです。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSProxy doesNotRecognizeSelector:name] called!'

テスト目的で NSManagedObject の前にインターフェイスを配置することを推奨する方法がいくつかあることは知っていますが、これは Mogenerator マシン/人間のファイルに加えて複雑さを増しているようです。

このコードを完全に再構成せずに、これに関する他の提案はありますか? このコードは既に運用されており、新しい機能を開発する際に単体テストを追加しようとしています。

4

1 に答える 1

3

問題の核心は、Core Data モデルがテストで使用できないことです。そのため、読み取りプロパティをスタブしようとすると、そのメソッドは存在しません。Core Data は、実行時にプロパティ アクセサーを動的にインターセプトします。

モデルを利用できるようにするには、.xcdatamodeld が単体テスト ターゲットに含まれていることを確認し、テストでモデルを設定する必要があります。動的プロパティをモックできるかどうかはわかりませんが、テストで Core Data オブジェクトに対して CRUD 操作を行うのは簡単になるため、それらをモックする必要はありません。テストでモデルを初期化する 1 つの方法を次に示します。

static NSManagedObjectModel *model;
static NSPersistentStoreCoordinator *coordinator;
static NSManagedObjectContext *context;
static NSPersistentStore *store;

-(void)setUp {
    [super setUp];
    if (model == nil) {
        @try {
            NSString *modelPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"my-model" ofType:@"mom"];
            NSURL *momURL = [NSURL fileURLWithPath:modelPath];
            model = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
        }
        @catch (NSException *exception) {
            NSLog(@"couldn't get model from bundle: %@", [exception reason]);
            @throw exception;
        }
    }
    coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    NSError *error;
    store = [coordinator addPersistentStoreWithType: NSInMemoryStoreType
                                      configuration: nil
                                                URL: nil
                                            options: nil
                                              error: &error];
    assertThat(store, isNot(nil));
    context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:coordinator];
}

-(void)tearDown {
    // these assertions ensure the test was not short-circuited by a failure to initialize the model
    assertThat(model, isNot(nil));
    assertThat(context, isNot(nil));
    assertThat(store, isNot(nil));
    assertThat(coordinator, isNot(nil));
    NSError *error = nil;
    STAssertTrue([coordinator removePersistentStore:store error:&error],
                 @"couldn't remove persistent store: %@", [error userInfo]);
    [super tearDown];
}

または、 MagicalRecordを使用して大幅に簡素化することもできます。アプリで使用しない場合でも、テストで使用して、すべてのコア データ セットアップをカプセル化できます。MagicalRecord を使用したアプリでの単体テストのセットアップは次のようになります。

-(void)setUp {
    [super setUp];
    [MagicalRecordHelpers setupCoreDataStackWithInMemoryStore];
}

-(void)tearDown {
    [MagicalRecordHelpers cleanUp];
    [super tearDown];
}
于 2012-10-02T23:28:19.670 に答える