2013-06-23 149 views
5

我想創建一個集成測試,它顯示某個動作導致顯示一個模態視圖控制器。故事板設置了2個視圖控制器,其中一個具有自定義ViewController類,第二個具有默認的UIViewController類和標題「second」。 segue設置爲模態,標識符爲「modalsegue」。在模擬器中運行應用程序效果出色,但我在定義正確的測試時遇到了很多問題。segues的自動化測試

ViewController.m:

@implementation ViewController 

- (IBAction)handleActionByPerformingModalSegue { 
    [self performSegueWithIdentifier:@"modalsegue" sender:self]; 
} 
@end 

測試:

- (void)testActionCausesDisplayOfSecondViewController { 
    ViewController * vc = 
     [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil] 
      instantiateViewControllerWithIdentifier:@"ViewController"]; 

    [vc handleActionByPerformingModalSegue]; 
    STAssertEquals(vc.presentedViewController.title, @"second", 
     @"Title of presented view controller should be second but is %@", 
     vc.presentedViewController.title, nil); 
} 

運行下面的輸出測試結果:

2013-06-23 17:38:44.164 SeguesRUs[15291:c07] Warning: Attempt to present <UIViewController: 0x7561370> on <ViewController: 0x7566590> whose view is not in the window hierarchy! 
SeguesRUsTests.m:33: error: -[SeguesRUsTests testActionCausesDisplayOfSecondViewController] : '<00000000>' should be equal to '<9c210d07>': Title of presented view controller should be second but is (null) 

我在做什麼錯?有沒有簡單的方法來避免第一條消息?

+0

好,你應該切換到一個更高層次的框架將實際測試的用戶界面,而不是內部的一些方法 - 弗蘭克 - 黃瓜例如蘋果的UI自動化。 – Sulthan

回答

0

這是我做的。 假設我已經連接了手動觸發的Segue(DocumentsDetailVC)的DocumentsVC。 下面是我的設置,然後我測試1.存在的segue,然後2.我強制視圖控制器(在我的情況下,我張貼通知)觸發其performSegueWithIdentifier並攔截prepareForSegue方法,看看是否一切爲新的視圖控制器(DocumentsDetailVC)被設置。這包括方法swizzling。

我沒有說我使用OCHamcrest/OCMockito進行單元測試,並且所有我的segues都是以「Segue」([self appDelegate] segueIdentifierForClass:[SomeClass class]])添加的目標視圖控制器命名的。

- (void)setUp 
{ 
    [super setUp]; 

    _isPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad; 

    realPrepareForSegue = @selector(prepareForSegue:sender:); 
    testPrepareForSegue = @selector(documentsBrowserTest_prepareForSegue:sender:); 

    UIStoryboard *storyboard = nil; 
    if (_isPad) { 
    storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil]; 
    } 
    else { 
    storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard_iPad" bundle:nil]; 
    } 
    UINavigationController *navController = [storyboard instantiateInitialViewController]; 
    self.sut = (DocumentsBrowserVC *)navController.topViewController; 
    [self.sut view]; 
} 


- (void)test_DocumentsDetailsVCSegueConnected 
{ 
    if (_isPad == FALSE) { 
    STAssertNoThrow([self.sut performSegueWithIdentifier:[[self appDelegate] segueIdentifierForClass:[DocumentsDetailVC class]] sender:self], @"DocumentsDetailVC should be connected"); 
    } 
} 


- (void)test_providerDidSelectPathLevelObject_triggersDocumentsDetailsVCSegueSectionIdFile 
{ 
    [DocumentsBrowserTest swapInstanceMethodsForClass:[DocumentsBrowserVC class] 
               selector:realPrepareForSegue 
              andSelector:testPrepareForSegue]; 

    [[NSNotificationCenter defaultCenter] addObserver:self.sut selector:@selector(providerDidSelectPathLevelObject:) name:ProviderDidSelectPathLevelObjectNotification object:nil]; 

    // when  
PathLevelObject *plo = self.pathLevelObjects[SectionIdFile][4]; 
NSDictionary *userInfo = @{OBJECT_KEY : plo , BROWSER_AREA_KEY : @(DocumentsFolder)}; 
[[NSNotificationCenter defaultCenter] postNotificationName:ProviderDidSelectPathLevelObjectNotification object:nil userInfo:userInfo]; 

    // then 
    if (_isPad == FALSE) { 
    assertThat(NSStringFromClass([objc_getAssociatedObject(self.sut, storyboardSegueKey) class]), is(equalTo(@"UIStoryboardPushSegue"))); 
    assertThatBool([[objc_getAssociatedObject(self.sut, storyboardSegueKey) destinationViewController] isKindOfClass:[DocumentsDetailVC class]], is(equalToBool(TRUE))); 
    assertThat(objc_getAssociatedObject(self.sut, senderKey), is(equalTo(self.sut))); 
    } 
    else { 
    assertThatInteger(self.sut.detailViewController.browsingArea, is(equalToInteger(DocumentsFolder))); 
    assertThat(self.sut.detailViewController.pathLevelObject, is(equalTo(plo))); 
    } 


    [[NSNotificationCenter defaultCenter] removeObserver:self.sut]; 

    [DocumentsBrowserTest swapInstanceMethodsForClass:[DocumentsBrowserVC class] 
               selector:realPrepareForSegue 
              andSelector:testPrepareForSegue]; 
} 
+0

這是很多用於驗證相對較少的測試代碼。你會說你從這樣的測試中獲得了很多價值嗎? – fatuhoku

1

隨着錯誤消息指出,問題是,你要出現在一個UIViewController,其觀點是不是在UIWindow層次。

最簡單的方法來解決它:

- (void)testExample { 

    // 
    // Arrange 
    // Storyboard 
    // 
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; 

    // 
    // Arrange 
    // View Controller 
    // 
    UIViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"ViewController"]; 
    [UIApplication sharedApplication].keyWindow.rootViewController = viewController; 

    // 
    // Act 
    // 
    [viewController performSegueWithIdentifier:@"ModalSegue" sender:nil]; 

    // 
    // Assert 
    // 
    XCTAssertEqualObjects(viewController.presentedViewController.title, @"Second"); 

} 
+0

'[UIApplication sharedApplication]'是否會在單元測試中初始化一個UIApplication實例?那裏會發生什麼? – fatuhoku

+0

我正在使用'UIApplication'的共享實例來訪問'keyWindow',以便我可以設置它的'rootViewController'。 –