以编程方式在UIAlertController上触发UIAlertAction吗?

Luk*_*uke 6 unit-testing swift uialertcontroller uialertaction

关于此主题有两个现有的问题,但它们并不是我所追求的。我为我的应用程序编写了一个Swift应用程序评级提示,其中显示了两个UIAlertController实例,一个实例由另一个实例触发。

我现在正在尝试对此进行单元测试,并试图在测试中达到第二个警报。我编写了一个简单的间谍程序来检查第一个控制器,但是我想一种触发第一个警报的动作的方法,该警报又显示第二个。

我已经尝试过了alert.actions.first?.accessibilityActivate(),但是似乎并没有在该操作的处理程序中打断–这就是我所追求的。

小智 7

一个不涉及更改生产代码以允许在单元测试中以编程方式轻敲UIAlertActions的解决方案,我在此SO答案中找到了该解决方案

在谷歌搜索寻找答案时,在这里出现的问题以及此问题都会弹出,下面的解决方案使我有更多的时间查找。

将扩展名放在测试目标下面:

extension UIAlertController {
    typealias AlertHandler = @convention(block) (UIAlertAction) -> Void

    func tapButton(atIndex index: Int) {
        guard let block = actions[index].value(forKey: "handler") else { return }
        let handler = unsafeBitCast(block as AnyObject, to: AlertHandler.self)
        handler(actions[index])
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是选定的答案 (4认同)
  • 谢谢,工作!由于按钮的索引可能会更改,因此您可以通过标题来标识它们,例如:`func tapButton(title:String){保护let action = actions.first(其中:{$ 0.title == title}),let block = action .value(forKey:“ handler”)else {return}让处理程序= unsafeBitCast(作为AnyObject阻止,发送至:AlertHandler.self)处理程序(操作)}` (2认同)

Luk*_*uke 5

这大致就是我所做的:

  1. 创建类的模拟版本以显示警报控制器,并在我的单元测试中使用此模拟。

  2. 覆盖我在非模拟版本中创建的以下方法:

    func alertActionWithTitle(title: String?, style: UIAlertActionStyle, handler: Handler) -> UIAlertAction
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在重写的实现中,将有关操作的所有详细信息存储在某些属性中(Handler只是typealias'd () -> (UIAlertAction)

    var didCreateAlert = false
    var createdTitles: [String?] = []
    var createdStyles: [UIAlertActionStyle?] = []
    var createdHandlers: [Handler?] = []
    var createdActions: [UIAlertAction?] = []
    
    Run Code Online (Sandbox Code Playgroud)
  4. 然后,在运行测试时,为了遍历警报的路径,我实现了一种callHandlerAtIndex方法来遍历处理程序并执行正确的处理程序。

这意味着我的测试看起来像这样:

feedback.start()
feedback.callHandlerAtIndex(1) // First alert, second action
feedback.callHandlerAtIndex(2) // Second alert, third action
XCTAssertTrue(mockMailer.didCallMail)
Run Code Online (Sandbox Code Playgroud)