iOS单元测试:如何设置/更新/检查firstResponder?

Jon*_*eid 17 iphone testing ocunit first-responder ios

你如何编写第一响应者单元测试?

我正在尝试编写测试以确认方法将焦点提升到下一个文本字段.controller是...的后代UIViewController.但是这个探索性测试失败了:

- (void)testFirstResponder
{
    [controller view];
    [[controller firstTextField] becomeFirstResponder];

    STAssertTrue([[controller firstTextField] isFirstResponder], nil);
}
Run Code Online (Sandbox Code Playgroud)

第一行导致视图被加载,以便其出口就位.文本字段是非零的.但测试从未通过.

我猜测becomeFirstResponder不会立即设置第一个响应者,而是将其安排在以后.那么有一个很好的方法来编写单元测试吗?

在接受的答案中从评论中提取答案......让事情运行一小段时间:

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
Run Code Online (Sandbox Code Playgroud)

我还发现我需要创建一个UIWindow并将视图控制器的视图放入其中,如最热门的答案中所述.

JRG*_*per 22

使用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]);
}
Run Code Online (Sandbox Code Playgroud)

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

Jon和Sergio提到您[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]]在调用becomeFirstResponder所需的子视图后可能需要调用,但我发现在我们的实例中不需要这样做.

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

  • 从 iOS 10.3 开始,这可以在不将其设为关键窗口的情况下工作。 (2认同)

ser*_*gio 8

我想在更新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];
 }
Run Code Online (Sandbox Code Playgroud)

注意:我使用了0.0延迟,因为我只是希望将消息放在事件队列中并尽快调度.我需要一种方法来回到主循环,因为它的内务管理.在您的情况下,这不会产生任何实际延迟.如果您正在执行多个相同类型的测试,即通过反复更改作为第一响应者的控件,此技术应该保证所有这些事件与生成的那些事件正确排序performSelector.

如果您从不同的线程运行测试,则可以使用 – performSelectorOnMainThread:withObject:waitUntilDone:

  • 是的,这不起作用......作为进一步的提示,我刚刚发现你可以在测试条件之前运行一段时间运行循环:`[[NSRunLoop currentRunLoop] runUntilDate:fiveSecondsFromNow];`(from:http ://stackoverflow.com/questions/1077737/ocunit-test-for-protocols-callbacks-delegate-in-objective-c).我不完全确定这个解决方案真的很"优雅",但它应该可以工作并且可能适用于"较小"的情况(当你真的不需要和Frank一起使用时)...... (2认同)

ada*_*dam 7

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

如果视图的window属性包含UIWindow对象,则它已安装在视图层次结构中; 如果它返回nil,则视图将从任何层次结构中分离.

希望这有助于....