segues的自动化测试
Posted
技术标签:
【中文标题】segues的自动化测试【英文标题】:Automated testing of segues 【发布时间】:2013-06-23 15:43:09 【问题描述】:我想创建一个集成测试,显示某个操作会导致显示模态视图控制器。故事板设置有 2 个视图控制器,一个具有自定义 ViewController 类,第二个具有默认 UIViewController 类和标题“second”。 segue 设置为具有标识符“modalsegue”的模态。在模拟器中运行应用程序效果很好,但我在定义正确的测试时遇到了很多麻烦。
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)
我做错了什么?有没有一种简单的方法可以避免第一条消息?
【问题讨论】:
好吧,您可能应该切换到更高级别的框架,该框架将实际测试 UI 而不是一些内部方法 - 例如 Apple 的 frank-cucumber 的 UI 自动化。 【参考方案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");
【讨论】:
[UIApplication sharedApplication]
不是要在单元测试中初始化 UIApplication 的实例吗?那里会发生什么?
我正在使用UIApplication
的共享实例来访问keyWindow
,以便我可以设置它的rootViewController
。【参考方案2】:
这就是我所做的。 假设我连接了带有手动触发 segue (DocumentsDetailVC) 的 DocumentsVC。 下面是我的设置,然后我测试 1. segue 的存在,然后 2. 我强制视图控制器(在我的情况下我发布通知)触发其 performSegueWithIdentifier 并拦截 prepareForSegue 方法以查看是否所有新的视图控制器(DocumentsDetailVC)已设置。这包括方法调配。
我没有使用 OCHamcrest/OCMockito 进行单元测试,并且我所有的 segue 都以“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];
【讨论】:
这是很多测试代码,用于验证相对较少。你会说你从这样的测试中获得了很多价值吗?以上是关于segues的自动化测试的主要内容,如果未能解决你的问题,请参考以下文章
单元测试:ViewController 没有带有标识符的 segue:“segue id”