0

我正在使用 swift 为 macOS 开发一个应用程序。我的应用程序中有一个可以启用或禁用的警报对象。我已经创建了一个从警报的启用属性到用户界面中的复选框的 Cocoa 绑定。为了验证绑定是否有效,我在警报启用状态的 didSet 属性观察者方法中添加了一条打印语句,当复选框切换时,该消息确实会打印出来。

我现在想编写一个单元测试以确保在启用属性被切换时实际发生正确的操作,并且我很难在单元测试中以编程方式创建一个绑定,该绑定将模拟在 Interface Builder 中创建的绑定所发生的情况。

测试看起来像这样:

func testAlarmTogglesEnabledState() {
    let alarm = Alarm()
    let enabler = AlarmEnabler(alarm)

    enabler.enable()

    XCTAssertTrue(alarm.enabled)
}

该测试实际上比这更复杂,因为警报实际上会采取一些措施来启用或禁用自身,但是,这有望从本质上说明正在发生的事情。

AlarmEnabled 类如下所示:

@objc private class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        super.init()
        let name = NSBindingName(rawValue: "enabled")
        bind(name, to: alarm, withKeyPath: "enabled", options: nil)
    }

    func enable() {
        enabled = true
    }

    @objc dynamic var enabled = false
}

警报类具有这种形式:

@objc class Alarm: NSObject {
    …

    @objc private(set) var enabled = true {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }

    …
}

测试运行时从不打印 didSet 属性观察器中的消息,因此编程绑定显然不起作用。

这是我尝试编写的一个游乐场,它基本上完成了单元测试试图完成的工作:

import Cocoa

@objc class Alarm: NSObject, Codable {
    init(_ enabled: Bool = true) {
        self.enabled = enabled
    }

    @objc dynamic var enabled: Bool {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }
}

@objc class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        self.alarm = alarm
        super.init()
        let name = NSBindingName(rawValue: #keyPath(AlarmEnabler.enabled))
        bind(name, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
//      bind(name, to: alarm, withKeyPath: #keyPath(AlarmEnabler.enabled), options: nil)
        checkBox.bind(NSBindingName.value, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
    }

    deinit {

    }

    func enable() {
        enabled = true
        checkBox.state = .on
    }

    func disable() {
        enabled = false
        checkBox.state = .off
    }

    @objc func checkBoxToggled(_ sender: NSButton) {
    }

    let checkBox = NSButton(checkboxWithTitle: "", target: self, action: #selector(AlarmEnabler.checkBoxToggled))
    @objc dynamic var enabled = false
    let alarm: Alarm
}

let alarm = Alarm(false)
let enabler = AlarmEnabler(alarm)

enabler.enable()
enabler.disable()

它将警报正确绑定到一个,并尝试通过使用复选框来模拟用户界面中发生的事情。这些似乎都无法改变属性。什么可以让操场代码正常工作?

4

1 回答 1

0

好的,多亏了 Willeke,我能够创建一个可以工作的游乐场,并且我还能够编写一个单元测试并让它通过。这是一个可以正常工作的 Playground 版本:

import Cocoa

@objc class Alarm: NSObject, Codable {
    init(_ enabled: Bool = true) {
        self.enabled = enabled
    }

    @objc private(set) var enabled: Bool {
        didSet {
            print("Alarm's enabled state was set to \(enabled)")
        }
    }
}

@objc class AlarmEnabler: NSObject {
    init(_ alarm: Alarm) {
        super.init()
        checkBox.bind(NSBindingName.value, to: alarm, withKeyPath: #keyPath(Alarm.enabled), options: nil)
    }

    func enable() {
        checkBox.state = .on
        checkBox.sendAction(checkBox.action, to: checkBox.target)
    }

    func disable() {
        checkBox.state = .off
        checkBox.sendAction(checkBox.action, to: checkBox.target)
    }

    @objc func checkBoxToggled(_ sender: NSButton) {
    }

    let checkBox = NSButton(checkboxWithTitle: "", target: self, action: #selector(AlarmEnabler.checkBoxToggled))
}

let alarm = Alarm(false)
let enabler = AlarmEnabler(alarm)

enabler.enable()
enabler.disable()
于 2019-03-20T00:20:00.350 回答