2013-06-23 144 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'。 –