可以测试IBAction吗?

时间:2013-09-09 13:38:49

标签: ios xctest ocunit

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

7 个答案:

答案 0 :(得分:10)

对于完整的单元测试,每个插座/操作需要三次测试:

  1. 插座是否挂在了一个视野?
  2. 插座是否与我们想要的动作相关联?
  3. 直接调用操作,就像它已被插座触发一样。
  4. 我一直这样做TDD我的视图控制器。您可以看到示例in this screencast

    听起来你要特别询问第二步。以下是单元测试的示例,用于验证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:"]);
    }
    

答案 1 :(得分: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,测试将失败,并显示错误“未调用预期方法”。

答案 2 :(得分:4)

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



    func checkCollision() {
        if ConChimCu.frame.origin.x == Trung.frame.origin.x { 
            txtBai.text = "You are the winner!"
        }
    }

然后我只是从测试中调用它:

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
}

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

如果unwrappedButton不存在,这意味着插座不存在,你也可以通过添加一些其他子句来使它更复杂一些。由于我喜欢将出口和行动测试分开,所以我没有将其包括在内

答案 3 :(得分: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
}

答案 4 :(得分:1)

因此,可能可以从故事板或笔尖实例化视图控制器,然后在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:

答案 5 :(得分: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
    }

答案 6 :(得分:-1)

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

由于此问题已变成对测试方法的调查,因此还有一个问题:如果您想测试操作应用UI的结果,请查看UI Automation tool in Instruments