Ios 可以测试IBAction吗?
单元测试IBOutlets有点容易,但是IBActions呢?我试图找到一种方法,但没有任何运气。有没有办法对视图控制器中的iAction和nib文件中的按钮之间的连接进行单元测试 因此,可能可以从故事板或nib实例化视图控制器,然后在UIButton上进行触摸。但是,我不会这样做,因为您正在测试一个苹果股票API。相反,我会直接调用该方法进行测试。例如,如果您的视图控制器中有一个方法Ios 可以测试IBAction吗?,ios,xctest,ocunit,Ios,Xctest,Ocunit,单元测试IBOutlets有点容易,但是IBActions呢?我试图找到一种方法,但没有任何运气。有没有办法对视图控制器中的iAction和nib文件中的按钮之间的连接进行单元测试 因此,可能可以从故事板或nib实例化视图控制器,然后在UIButton上进行触摸。但是,我不会这样做,因为您正在测试一个苹果股票API。相反,我会直接调用该方法进行测试。例如,如果您的视图控制器中有一个方法-(iAction)foo:(id)sender,您需要测试该方法中的逻辑,我会这样做: MyViewContr
-(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未连接,测试将失败,错误为“未调用预期方法”。对于完整单元测试,每个出口/操作需要三个测试:
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的翻译