UISlider addTarget和sendActions不适用于UT

时间:2017-05-19 15:17:38

标签: ios swift unit-testing xctest

当我在标准MVC项目中运行此代码时,一切正常:我可以通过编程方式将动作发送到我的UISlider。

override func viewDidLoad() {
    super.viewDidLoad()        
    slider = UISlider(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
    slider.minimumValue = 1
    slider.maximumValue = 100
    slider.addTarget(self, action: #selector(sliderTouch), for: .touchUpInside)
    slider.sendActions(for: .touchUpInside)
}

func sliderTouch(sender: UISlider) {
    print("value: \(sender.value)")
}

现在由于某种原因,当我想在我的UT中模拟Slider行为时,它不起作用。这是代码:

func testSlider() {
    let slider = UISlider(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
    slider.minimumValue = 1
    slider.maximumValue = 100
    slider.addTarget(self, action: #selector(sliderTouch), for: .touchUpInside)
    slider.sendActions(for: .touchUpInside)
}

func sliderTouch(sender: UISlider) {
    print("value: \(sender.value)")
}
在UT情况下永远不会调用

sliderTouch(),根据我的理解,sendActions不是asnyc,应直接调用操作方法。

那么为什么我会得到这种行为,我该如何解决这个问题?

修改:我还需要它可以用于其他UIKit控件,例如UISwitchUISegmentedControlUIDatePicker等...

2 个答案:

答案 0 :(得分:5)

在测试Framework目标中包含的类时,我遇到了与您类似的问题。

以下摘自Apple's documentation

  

当发生控件特定事件时,控件立即调用任何关联的操作方法。通过当前的UIApplication对象调度Action方法,该对象根据需要在响应者链之后找到处理消息的适当对象。

在框架中测试类时,没有UIApplication对象,因此无法调度事件。

您可以在测试目标设置中添加主机应用程序。如果您没有主机应用程序,则可以创建一个空iOS应用程序并使用它。

另见this post

<强>更新

以下是如何设置主机应用程序

Xcode Screenshot

如果下拉列表中没有应用程序,只需创建一个新目标并使用应用程序&gt; 单一视图应用模板。

你已经完成了,你的测试现在应该可行了。没有其他步骤。

答案 1 :(得分:1)

对于swift 3: https://github.com/ReactiveCocoa/ReactiveCocoa/blob/ae6ebb7725b3d5d33db039d456797c220720cb99/ReactiveCocoaTests/UIKit/UIControl%2BEnableSendActionsForControlEvents.swift

对于Swift 2 https://github.com/RACCommunity/Rex/blob/master/Tests/Helpers/UIControl%2BEnableSendActionsForControlEvents.swift

或尝试子类化:

class TouchSlider: UISlider {
    var touchUpInsideHandler: ((TouchSlider) -> Void)?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        touchUpInsideHandler?(self)
        guard let touch = touches.first,
              let imageWidth = currentThumbImage?.size.width else {
            super.touchesBegan(touches, with: event)
            return
        }
        let location = touch.location(in: self)
        let newValue: Float = minimumValue + (maximumValue - minimumValue) * Float((location.x - imageWidth / 2.0) / (bounds.width - imageWidth))
        setValue(newValue, animated: true)
        super.touchesBegan(touches, with: event)
    }
}

使用touchUpInsideHandler管理外面的动画。