iOSunit testing:如何设置/更新/检查firstResponder?

你如何编写第一响应unit testing?

我试图写一个testing来确认一个方法将焦点推进到下一个文本字段。 controllerUIViewController的后代。 但是这个探索性testing失败了:

 - (void)testFirstResponder { [controller view]; [[controller firstTextField] becomeFirstResponder]; STAssertTrue([[controller firstTextField] isFirstResponder], nil); } 

第一行导致视图被加载,以便其网点到位。 文本字段是非零。 但testing永远不会通过。

我猜想, becomeFirstResponder不会立即设置第一响应者,但安排它以后。 那么是否有一个很好的方法来写一个unit testing呢?

从已接受的答案中的评论中拉出答案让事情运行一小段时间:

 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]; 

我想pipe理/更改第一个响应者链是在主循环中完成的,当UI更新准备下一个事件处理时。 如果这个假设是正确的,我会简单地做到以下几点:

 -(void)assertIfNotFirstResponder:(UITextField*)field { STAssertTrue([field isFirstResponder], nil); } - (void)testFirstResponder { [controller view]; [[controller firstTextField] becomeFirstResponder]; [self performSelector:@selector(@"assertIfNotFirstResponder:") withObject:[controller firstTextField] afterDelay:0.0]; } 

注意:我使用了0.0延迟,因为我只是希望消息放在事件队列中并尽快分派。 我只需要一种方式来回到主循环,为其pipe家。 这不应该在你的情况下产生实际的延迟。 如果您正在执行多个相同types的testing,即通过反复更改作为第一响应者的控件,此技术应确保所有这些事件与performSelector生成的事件正确sorting。

如果你正在从不同的线程运行你的testing,你可以使用– performSelectorOnMainThread:withObject:waitUntilDone:

使用Xcode 5.1和XCTestCase ,这似乎工作正常:

 - (void)testFirstResponder { // Make sure the controller's view has a window UIWindow *window = [[UIWindow alloc] init]; [window addSubview:controller.view]; // Call whatever method you're testing [controller.textView becomeFirstResponder]; // Assert that the desired subview is the first responder XCTAssertTrue([sut.textView isFirstResponder]); } 

为了使视图/子视图成为第一响应者,它必须是视图层次结构的一部分 ,这意味着它的根视图的窗口属性必须被设置。

乔恩和塞尔吉奥提到,你可能需要在你想要的子视图上调用becomeFirstResponder后调用[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]] ,但是我发现在我们的实例中这不是必需的。

但是,您的里程可能会有所不同(甚至取决于您使用的Xcode版本),因此您可能需要也可能不需要包含此类呼叫。

您需要确保textField安装在视图层次结构中。

如果视图的窗口属性包含一个UIWindow对象,则它已经安装在视图层次结构中; 如果返回nil,则视图将从任何层次结构中分离出来。

希望这可以帮助….

看来,而不是unit testing,这是自动验收testing的工作。 (请参阅Frank:针对iPhone和iPad的自动验收testing )