2012-10-02 50 views
1

我使用OCMock模擬NSManagedObjects在我的單元測試。我使用Mogenerator爲我的核心數據對象生成機器和人類可讀文件。我試圖嘲笑NSManagedObject返回一個布爾值和一個字符串。兩者都是我的核心數據實體的屬性。當我嘲笑BOOL時,該值從對象正確返回,我的類使用它,並且測試成功通過。當我嘗試在同一對象上存根NSString屬性時,它將引發NSInvalidArgumentException [NSProxy doesNotRecognizeSelector]OCMock與核心數據/ Mogenerator拋出異常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!' 

我知道有some recommendations有,在NSManagedObject前用於測試目的坐鎮的接口,但這似乎增加了Mogenerator機器/人類文件的複雜性。

有沒有其他的建議,而不是完全重新配置這段代碼?這段代碼已經投入使用,我們正在嘗試在開發新功能時添加單元測試。

回答

3

問題的癥結在於您的Core Data模型在測試中不可用,所以當您嘗試對屬性讀取進行存根時,該方法不存在。核心數據在運行時動態攔截屬性訪問器。

要使您的模型可用,您需要確保您的.xcdatamodeld包含在您的單元測試目標中,並且您需要在測試中設置模型。我不確定你能否模擬動態屬性,但在測試中對核心數據對象執行CRUD操作變得微不足道,所以不需要嘲笑它們。下面就來初始化模型在測試中的一種方式:

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]; 
} 
+0

我想我是在推翻這一個。我在內存存儲器中使用MagicalRecord進行測試,並且在每次測試之前重置db,就像您注意到的那樣。我應該剛剛使用託管對象而不是嘲笑它們。感謝您的徹底解答! –