如何通过编程方式伪造一个触摸事件到UIButton?

200

我正在编写一些单元测试,由于这个特定应用的性质,重要的是我尽可能地深入到UI层面。因此,我想以编程方式触发按钮按下事件,就像用户在GUI中按下按钮一样。

(是的,我可以调用IBAction选择器,但是这个特定应用的性质使得我必须伪造实际的按钮按下操作,以便从按钮本身调用IBAction)

怎么做才是首选方法呢?


你能解释一下为什么伪造按钮触摸而不是直接调用动作方法很重要吗?实际上并没有什么实质性的区别。 - Jasarien
1
@Jasarien:嗯,因为确实有区别!在我的应用程序中,几个按钮使用相同的操作方法,该方法根据按钮的标记解析如何行为。因此,单元测试除了测试通过方法的一个特定代码路径之外,(a)需要测试它们所有的内容,并且(b)还验证按钮是否正确连接。此外,它可以很好地抽象出我的单元测试套件,因为我可以将单元测试定义为文本文件中的行,例如:“1 + 2 = 3”,其中“3”是预期的结束显示,而在它之前的所有内容都是要按哪个按钮。 - Olie
你的动作方法没有一个 (id)sender 参数吗?你为什么不直接调用这个方法并将按钮作为参数传递进去呢? - Jasarien
3
由于我的测试工具很抽象,我不知道特定按钮需要调用哪个方法。我想我可以深入研究该按钮并找到它,但使用sendActionsForControlEvents:可以为我完成所有这些操作,减少错误的机会,并保持测试的抽象性和与设计的契合度。 - Olie
9个回答

483

结果是

[buttonObj sendActionsForControlEvents:UIControlEventTouchUpInside];

这个确切地给了我我所需要的东西。

编辑: 别忘了在主线程中执行,以获得类似用户按下的结果。


针对Swift 3:

buttonObj.sendActions(for: .touchUpInside)

14
你在回复评论时非常克制。 ;) - Dan Rosenstark
2
不幸的是,这两种方法并不总是有效 - 它们依赖于接收器是UIControl,而不是例如具有轻拍手势。 - tooluser
@Olie,你说得完全正确,我重新阅读后也不知道当时在想什么。我唯一的猜测是,我认为这对UIBarButtonItems无效,因为它们只是按钮的包装器,而不是UIControls。……呼。 - tooluser
1
@ChenLiYong,一个原因可能是myActionMethod:不是公共的,因此您无法直接调用它。 - Iulian Onofrei
1
Xamarin: buttonObj.SendActionForControlEvents(UIControlEvent.TouchUpInside); Xamarin:buttonObj.SendActionForControlEvents(UIControlEvent.TouchUpInside); - Pierre
显示剩余9条评论

52

关于Swift的更新

buttonObj.sendActionsForControlEvents(.TouchUpInside)

编辑: 已更新为 Swift 3 版本。

buttonObj.sendActions(for: .touchUpInside)

1
我会说:buttonObj.sendActionsForControlEvents(.TouchUpInside) 更符合Swift的风格 ;) - Sajjon
1
我认为如果在不支持运行循环的XCTestCase中创建按钮,这将无法正常工作。 - CopperCash

7

Swift 3:

self.btn.sendActions(for: .touchUpInside)

5
这只是在重复得分更高的答案。 - Pang

6

如果您想进行这种类型的测试,您会喜欢iOS 4中的UI自动化支持。您可以编写JavaScript来模拟按钮按下等操作,尽管文档(特别是入门部分)有点简略。


1
是的,那很酷,但我正在尝试从应用程序内部完成,而不是通过Instruments(如果您指的是该方法)。 - Olie
Instruments与您的应用程序一起运行。 应用程序仍在运行,Instruments只是模拟触摸。 - Jeff Kelley
6
我了解仪器,Jeff——我的意思是:我想让我的单元测试在口袋里的设备上运行,而不需要笔记本电脑、XCode或者仪器。或者你知道如何在设备上运行仪器吗?;) - Olie

5
在这种情况下,UIButton 派生自 UIControl。对于从 UIControl 派生的对象,这是有效的。
我想在特定用例中重用 "UIBarButtonItem" 动作。在这里,UIBarButtonItem 并没有提供方法 sendActionsForControlEvents: 但幸运的是,UIBarButtonItem 有目标和动作属性。
 if(notHappy){        
         SEL exit = self.navigationItem.rightBarButtonItem.action;
         id  world = self.navigationItem.rightBarButtonItem.target;
         [world performSelector:exit];
 }

在这里,rightBarButtonItem 是类型为 UIBarButtonItem 的对象。

如果你使用UIBarButtonItem,那你可以直接调用[self doAction:self.navigationItem.rightBarButtonItem];//-doAction:是self.navigationItem.rightBarButtonItem的切换动作名称。 - c0ming

5

针对 Xamarin iOS

btnObj.SendActionForControlEvents(UIControlEvent.TouchUpInside);

Reference


3

Swift 5:

class ViewController: UIViewController {
    @IBOutlet weak var theTextfield: UITextField!
    @IBOutlet weak var someButton: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        theTextfield.text = "Pwd"
        someButton.sendActions(for: .touchUpInside)
    }

    @IBAction func someButtonTap(_ sender: UIButton) {
        print("button tapped")
    }
}

2

对于那些编写单元测试而不是 UI 测试的人来说,这很方便;-)

在 Swift 5 中解决 UIBarButtonItem 的问题的方法,因为它没有像 UIButton 等控件一样的 sendAction 方法。

extension UIBarButtonItem {
    func sendAction() {
        guard let myTarget = target else { return }
        guard let myAction = action else { return }
        let control: UIControl = UIControl()
        control.sendAction(myAction, to: myTarget, for: nil)
    }
}

现在你可以简单地:
let action = UIBarButtonItem(title: "title", style: .done, target: self, action: #selector(doSomething))
action.sendAction()

-4

Swift 4:

self.yourButton(self)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接