Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/105.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 可以测试IBAction吗?_Ios_Xctest_Ocunit - Fatal编程技术网

Ios 可以测试IBAction吗?

Ios 可以测试IBAction吗?,ios,xctest,ocunit,Ios,Xctest,Ocunit,单元测试IBOutlets有点容易,但是IBActions呢?我试图找到一种方法,但没有任何运气。有没有办法对视图控制器中的iAction和nib文件中的按钮之间的连接进行单元测试 因此,可能可以从故事板或nib实例化视图控制器,然后在UIButton上进行触摸。但是,我不会这样做,因为您正在测试一个苹果股票API。相反,我会直接调用该方法进行测试。例如,如果您的视图控制器中有一个方法-(iAction)foo:(id)sender,您需要测试该方法中的逻辑,我会这样做: MyViewContr

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

因此,可能可以从故事板或nib实例化视图控制器,然后在UIButton上进行触摸。但是,我不会这样做,因为您正在测试一个苹果股票API。相反,我会直接调用该方法进行测试。例如,如果您的视图控制器中有一个方法
-(iAction)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:
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];
func testScheduleActionIsConnected() {
    XCTAssertTrue(checkActionForOutlet(controller.btnScheduleOrder, actionName: "scheduleOrder", event: UIControlEvents.TouchUpInside, controller: controller ))
}

我用OCMock做的,像这样:

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:
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];
func testScheduleActionIsConnected() {
    XCTAssertTrue(checkActionForOutlet(controller.btnScheduleOrder, actionName: "scheduleOrder", event: UIControlEvents.TouchUpInside, controller: controller ))
}

如果iAction未连接,测试将失败,错误为“未调用预期方法”。

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

  • 插座是否连接到视图上
  • 插座是否连接到我们想要的操作
  • 直接调用操作,就像它是由出口触发的一样
  • 我一直这样做是为了TDD我的视图控制器。你可以看到一个例子

    听起来你是在特别询问第二步。下面是一个单元测试的示例,验证了
    myButton
    内部的润色是否会调用动作
    doSomething:
    下面是我如何使用。(
    sut
    是测试系统的测试夹具。)

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

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

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


    由于这个问题已经变成了一个关于测试方法的调查,这里还有一个问题:如果你想测试操作应用程序UI的结果,请查看。

    以下是我在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
    }
    
    然后我从测试中调用它,如下所示:

    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:
    
    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];
    
    func testScheduleActionIsConnected() {
        XCTAssertTrue(checkActionForOutlet(controller.btnScheduleOrder, actionName: "scheduleOrder", event: UIControlEvents.TouchUpInside, controller: controller ))
    }
    
    我基本上是确保按钮btnScheduleOrder有一个iAction,它与事件触碰内部的名称scheduleOrder关联。我需要通过包含按钮的控制器来验证操作的目标


    如果unwrappedButton不存在,也就是说插座不存在,您还可以通过添加其他一些else子句使其更复杂一些。因为我喜欢将出口和行动测试分开,所以我没有将其包括在这里

    上面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
    }
    

    @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
        }
    

    你说的“测试”是什么意思?您想对这些进行单元测试吗?如果是这样的话,那么就将您的操作封装到函数/类中,并使用单元测试来测试这些类。如果您还有其他想法,请解释一下。是的,我想编写单元测试。我正在使用单元测试,因此如何检查此按钮是否正确,请使用-xctasertrue等。我正在使用segue on按钮,因此如何进行测试?为了这样做,您必须创建一个专门用于测试目的的
    IBOutlet
    。您还必须在字符串中引用处理程序,这意味着如果重构,它将中断。@jowie:AppCode会自动重构类似的东西。但是,即使没有AppCode,我也愿意付出很小的代价,因为我可以轻松快速地获得反馈。这将检查逻辑,但不会检查
    UIButton
    是否已在Interface Builder中连接。我似乎得到了“预期方法未被调用”,即使IBOutlet已连接。我甚至在
    viewdiload
    中有一个
    NSLog
    ,它显示了对
    ui按钮的正确引用。由于Abbey和Julio,我在下面为Swift 4添加了对Swift 3的翻译