是否可以测试IBAction?

对IBOutlets进行unit testing有点容易,但IBActions呢? 我试图找到一种方法,但没有任何运气。 有没有办法在View Controller中的IBAction和nib文件中的按钮之间进行unit testing连接?

对于完整的unit testing,每个sockets/操作需要三个测试:

  1. sockets是否挂在一起?
  2. sockets是否与我们想要的动作相关联?
  3. 直接调用操作,就像它已被sockets触发一样。

我一直这样做TDD我的视图控制器。 您可以在此截屏video中看到一个示例。

听起来你问的是第二步。 这是一个unit testing的例子,用于validationmyButton 修饰是否会调用doSomething:这是我使用OCHamcrest表达的方式 。 ( sut是被测系统的测试夹具。)

 - (void)testMyButtonAction { assertThat([sut.myButton actionsForTarget:sut forControlEvent:UIControlEventTouchUpInside], contains(@"doSomething:", nil)); } 

或者,这是一个没有Hamcrest的版本:

 - (void)testMyButtonAction { NSArray *actions = [sut.myButton actionsForTarget:sut forControlEvent:UIControlEventTouchUpInside]; XCTAssertTrue([actions containsObject:@"doSomething:"]); } 

我是用OCMock做的,像这样:

 MyViewController *mainView = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil]; [mainView view]; id mock = [OCMockObject partialMockForObject:mainView]; //testButtonPressed IBAction should be triggered [[mock expect] testButtonPressed:[OCMArg any]]; //simulate button press [mainView.testButton sendActionsForControlEvents: UIControlEventTouchUpInside]; [mock verify]; 

如果未连接IBAction,则测试将失败,并显示错误“未调用预期方法”。

这是我在Swift中使用的内容。 我创建了一个帮助函数,我可以在所有的UIViewControllerunit testing中使用它:

 func checkActionForOutlet(outlet: UIButton?, actionName: String, event: UIControlEvents, controller: UIViewController)->Bool{ if let unwrappedButton = outlet { if let actions: [String] = unwrappedButton.actionsForTarget(controller, forControlEvent: event)! as [String] { return(actions.contains(actionName)) } } return false } 

然后我就像这样从测试中调用它:

 func testScheduleActionIsConnected() { XCTAssertTrue(checkActionForOutlet(controller.btnScheduleOrder, actionName: "scheduleOrder", event: UIControlEvents.TouchUpInside, controller: controller )) } 

我基本上确保按钮btnScheduleOrder具有与事件TouchUpInside的名称scheduleOrder相关联的IBAction。 我需要传递包含按钮的控制器,以便validation操作的目标。

你也可以通过添加一些其他的子句来使它变得更复杂,以防unwrappedButton不存在,这意味着sockets不在那里。 由于我喜欢分开sockets和动作测试,所以我没有将它包括在内

因此,可能可以从故事板或笔尖实例化视图控制器,然后在UIButton上进行触摸。 但是,我不会这样做,因为您正在测试Apple股票API。 相反,我会通过直接调用该方法来测试。 例如,如果你的视图控制器中有一个方法- (IBAction)foo:(id)sender ,你需要测试该方法中的逻辑,我会这样做:

 MyViewController *viewController = [[MyViewController alloc] initWithNibName:@"NibName" bundle:[NSBundle mainBundle]]; UIButton *sampleButton = [[UIButton alloc] init]; [sampleButton setTitle:@"Default Storyboard Title" forState:UIControlStateNormal]; [viewController foo:sampleButton]; // Run asserts now on the logic in foo: 

Julio Bailon的上述答案翻译为Swift 3:

  func checkActionForButton(_ button: UIButton?, actionName: String, event: UIControlEvents = UIControlEvents.touchUpInside, target: UIViewController) -> Bool { if let unwrappedButton = button, let actions = unwrappedButton.actions(forTarget: target, forControlEvent: event) { var testAction = actionName if let trimmedActionName = actionName.components(separatedBy: ":").first { testAction = trimmedActionName } return (!actions.filter { $0.contains(testAction) }.isEmpty) } return false } 

这里有很多好的答案。 最适合您的方法取决于您的测试计划。

由于这个问题已经变成了对测试方法的调查,还有一个问题:如果你想测试操作应用程序UI的结果,请查看Instruments中的UI自动化工具 。