如何测试 UIControlEvents 是否已被触发

How to test if a UIControlEvents has been fired

我有一个实现自定义 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: 请注意,因为我正在测试一个库,所以我的测试目标没有任何主机应用程序。当测试目标有主机应用程序时,上面的测试通过。

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

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,否则它们都不会被调用。

Apple's documentation for UIControl 指出:

When a control-specific event occurs, the control calls any associated action methods right away. Action methods are dispatched through the current UIApplication object, which finds an appropriate object to handle the message, following the responder chain if needed.

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

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