【问题标题】:UISlider addTarget and sendActions not working in UTsUISlider addTarget 和 sendActions 在 UT 中不起作用
【发布时间】:2017-05-19 15:17:38
【问题描述】:

当我在标准 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)")
}

sliderTouch() 在 UT 案例中从未被调用,据我了解,sendActions 不是asnyc,应该直接调用操作方法。

那么为什么我会出现这种行为,我该如何解决呢?

编辑:我还需要它用于其他 UIKit 控件,例如 UISwitchUISegmentedControlUIDatePicker 等...

【问题讨论】:

  • 你想抓住机会吗?
  • 以什么方式测试您的组件与 UIKit 提供的动作调度功能?如果你想测试你的触摸处理,你可以直接调用action方法,比如sliderTouch(sender: slider)。 (这无助于澄清这一点,您的 testSlider 测试函数没有实际的测试断言,因此总是通过。)
  • @JeremyW.Sherman 我的用例不是上面的 sn-p。我刚刚写了 sn-p 来说明主要问题是什么。实际上,我在库中有一个自定义 Slider,它以某种方式对动作调度做出反应,我想测试该行为是否正确。我没有在上面的 sn-p 中写断言,所以我尽量不混淆你并保持问题清晰:)

标签: ios swift unit-testing xctest


【解决方案1】:

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

这是Apple's documentation的摘录:

当发生特定于控件的事件时,控件会立即调用任何关联的操作方法。 Action 方法通过当前的 UIApplication 对象进行调度,该对象会找到一个合适的对象来处理消息,如果需要,跟随响应者链。

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

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

另见this post

更新:

以下是设置主机应用程序的方法:

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

你已经完成了,你的测试现在应该可以工作了。没有额外的步骤。

【讨论】:

  • 这是正确的!我会考虑添加这个主机应用程序。如果有效,我会标记为正确
  • Vasili Muravev 在他的答案的 cmets 中指出了一种方法,并且似乎被 ReactiveCocoa 采用。它对我有用。你认为你的方法更好吗?让我知道,因为我找不到简单的方法来做到这一点。但无论如何,你的回答正确地给了我一个“为什么它不起作用”的答案。再次感谢您。
  • 当然,与任何主题一样,每种方法都有优点和缺点。话虽如此,我认为我的解决方案应该是首选解决方案,因为它不依赖任何额外的代码。方法调配是一件非常强大的事情,但如果您不完全了解发生了什么,它会带来很多陷阱。此外,对于使用相同代码的其他团队成员来说,如果发现自己处于 UIKit 方法实际上并没有像通常那样做的情况下,这可能会非常令人惊讶,因为您交换了它的实现。
  • 至于“简单的方法”,我将在我的帖子中添加一些屏幕截图,说明您需要做什么。
  • 我更新了我的帖子,提供了有关如何设置 Host 应用程序的更多详细信息。如果您还有其他问题,请告诉我。
【解决方案2】:

对于 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 管理外部触摸。

【讨论】:

  • 感谢您的解决方案。我担心的是,我不仅需要模拟UISlider,还需要模拟UIDatePickerUISwitch 和许多其他的。对于一个简单的问题,这将是很多子类化和样板文件,你不觉得吗?
  • @GuyDaher 你应该把你的问题具体化;)So why am I getting this behavior, and how can I solve this?
  • 你是对的,我的错。我认为有一个可以应用于所有 UIKit 控件的解决方案。我将编辑问题,谢谢您指出并再次抱歉
  • @GuyDaher 嗯,它对我有用:Test Case '-[BaseAppV2UnitTests.testTest testSleder]' started. value: 1.0 Test Case '-[BaseAppV2UnitTests.testTest testSleder]' passed (0.004 seconds).
猜你喜欢
  • 2017-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-27
相关资源
最近更新 更多