7

对 IBOutlets 进行单元测试有点容易,但是 IBActions 呢?我试图找到一种方法来做到这一点,但没有任何运气。有没有办法对视图控制器中的 IBAction 和 nib 文件中的按钮之间的连接进行单元测试?

4

7 回答 7

10

对于完整的单元测试,每个出口/动作都需要三个测试:

  1. 插座是否连接到视图?
  2. 出口是否连接到我们想要的动作?
  3. 直接调用该动作,就好像它已被插座触发一样。

我一直这样做是为了对视图控制器进行 TDD。您可以在此截屏视频中看到一个示例。

听起来您是在专门询问第二步。这是一个单元测试示例,用于验证内部修饰myButton是否会调用该操作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:"]);
}
于 2013-09-10T22:18:18.867 回答
5

我是用 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,则测试将失败并出现错误“未调用预期的方法”。

于 2013-09-09T15:46:00.053 回答
5

这是我在 Swift 中使用的。我创建了一个可以在所有 UIViewController 单元测试中使用的辅助函数:

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。我需要传递包含按钮的控制器作为验证操作目标的一种方式。

您还可以通过添加一些其他 else 子句使其更复杂一些,以防 unwrappedButton 不存在,这意味着插座不存在。因为我喜欢将出口和动作测试分开,所以这里没有包含它

于 2016-04-26T02:15:39.790 回答
2

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
}
于 2017-01-04T19:08:42.987 回答
1

因此,可能可以从情节提要或 nib 实例化视图控制器,然后在 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:
于 2013-09-09T13:51:11.540 回答
0

@AbbeyJackson Swift 3 版本更新到 Swift 4.2,感谢 @JulioBailon 提供原始版本。

 func checkActionForButton(_ button: UIButton?, actionName: String, event: UIControl.Event = UIControl.Event.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
    }
于 2019-02-28T15:05:59.127 回答
-1

这里已经有很多好的答案了。哪个最适合您取决于您​​的测试计划。

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

于 2013-09-11T00:18:01.090 回答