【问题标题】:How to test if a UIControlEvents has been fired如何测试 UIControlEvents 是否已被触发
【发布时间】:2017-02-12 13:19:13
【问题描述】:

我有一个实现自定义UIControl 的库,其方法在调用时会触发.valueChanged 事件。我想测试该行为的方法。

我的自定义控件:

class MyControl: UIControl {
    func fire() {
        sendActions(for: .valueChanged)
    }
}

还有测试:

import XCTest

class ControlEventObserver: NSObject {
    var expectation: XCTestExpectation!

    init(expectation anExpectation: XCTestExpectation) {
        expectation = anExpectation
    }

    func observe() {
        expectation.fulfill()
    }
}

class Tests: XCTestCase {
    func test() {
        let myExpectation = expectation(description: "event fired")
        let observer = ControlEventObserver(expectation: myExpectation)
        let control = MyControl()
        control.addTarget(observer, action: #selector(ControlEventObserver.observe), for: .valueChanged)
        control.fire()
        waitForExpectations(timeout: 1) { error in
            XCTAssertNil(error)
        }
    }
}

问题是 observe 方法永远不会被调用,所以 expectation 没有实现。

问题是:在这种情况下,我们如何测试UIControlEvents?也许我们需要以某种方式强制运行循环?

编辑 1: 请注意,由于我正在测试一个库,我的测试目标没有任何主机应用程序。当测试目标有宿主应用程序时,上述测试通过。

【问题讨论】:

  • 这很奇怪,我已将您的代码复制并粘贴到我创建的项目中,并且期望已满,测试成功。
  • 刚刚意识到这可能是因为我正在测试一个库。当我使用主机应用程序创建另一个测试目标时,上面的测试通过了。 @WilsonB​​alderrama

标签: ios swift selector xctest nsrunloop


【解决方案1】:

您在测试方法中声明了观察者对象。这意味着一旦方法完成,它将从内存中释放,因此不会被调用。在Tests 类中的类级别创建对观察者的引用,如下所示,它将起作用。

class Tests: XCTestCase {

    var observer: ControlEventObserver!
    func test() {
        let myExpectation = expectation(description: "event fired")
        self.observer = ControlEventObserver(expectation: myExpectation)
        let control = MyControl()
        control.addTarget(observer, action:#selector(ControlEventObserver.observe), for: .valueChanged)
        control.fire()
        waitForExpectations(timeout: 1) { error in
            XCTAssertNil(error)
        }
    }
}

您还需要以同样的方式声明 myExpectationcontrol,否则它们也不会被调用。

【讨论】:

  • 感谢您的回答。我认为对象没有被释放,因为我没有创建任何自动释放池。另外,我正在测试一个库,因此测试目标没有任何主机应用程序。我已经修改了我的问题以反映这一点。
【解决方案2】:

Apple's documentation for UIControl 声明:

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

当在 UIControl 上调用 sendActions(for:) 时,控件将调用 UIApplicationsendAction(_:to:from:for:) 以将事件传递给注册的目标。

因为我正在测试一个没有任何主机应用程序的库,所以没有 UIApplication 对象。因此,.valueChanged 事件不会被调度,observe 方法也不会被调用。

【讨论】:

  • 那你是如何模拟库中的点击事件的呢?
  • @User9527 我最终为我的测试套装创建了一个简单的主机应用程序
  • 你能告诉我这是怎么做到的吗@ThanhPham?我有一个关于 UIButton here 的类似问题
  • @Benjohn 我创建了一个应用程序目标,在测试目标的常规设置屏幕中,我将应用程序目标设置为测试目标的主机应用程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
相关资源
最近更新 更多