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