为什么在测试时,NSTextView子类的undoManager总是为nil?

移动开发 2026-07-09

我有一个NSTextView的子类(MyTextView)。当我尝试用Swift测试它的 undoManager 属性时,它总是 nil,测试失败。

// Added extension
extension MyTextView {
    override var undoManager: UndoManager? {
        _testingUndoManager
    }
}

@MainActor
struct Undo {

    let mtv: MyTextView

    init() throws {
        mtv = MyTextView()
        mtv.insertText("Lorem ipsum dolor sit amet")
        mtv._testingUndoManager = UndoManager() // <-- Added
        mtv.moveToBeginningOfDocument(self)
        mtv.moveWordRightAndModifySelection(self)
    }

    @Test func roundtrip() async throws {
        #expect(mtv.selections == [(0, 5)])
        mtv.delete(self)
        #expect(mtv.selections == [(0, 0)])
        #expect(mtv.allowsUndo)
        mtv.undoManager?.undo() // undoManager is always nil
        #expect(mtv.selections == [(0, 5)])
        let strings = mtv.getSelectedText()
        #expect(strings == ["Lorem"])
    }
}

显然测试并没有把完整的机制(运行循环?)设置好。有没有办法在测试期间强制实例化一个撤销管理器(undo manager)?

编辑: 我已经把上面的代码更新为到目前为止我找到的唯一解决方案,以防将来遇到同样情况的朋友。不幸的是,这需要在 NSTextView 子类中添加一个实例变量:

var _testingUndoManager: UndoManager? = nil

解决方案

你所看到的问题是AppKit的预期行为,NSUndoManagerNSTextView 只有在视图在真实的 NSWindow 中创建时才会被创建。在单元测试场景中,视图仅存在于内存中,测试用例中不存在 NSWindow,因此没有响应者链,导致undoManager返回nil。一个解决方案就是你已经在做的那个,另一种解决方案是通过编程方式创建 NSWindow 并将其附加到你的 NSView

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章