我正在使用Xcode在具有com.apple.security.files.user-selected.read-write
授权(即,可以通过NSOpenPanel
GUI明确访问用户所选择的文件和文件夹)的沙盒macOS应用上进行UI测试。 / p>
我注意到模态显示打开的面板后,代码覆盖立即停止。这是我的代码:
@IBAction func go(_ sender: Any) {
let panel = NSOpenPanel()
panel.canCreateDirectories = true
panel.canChooseDirectories = true
panel.canChooseFiles = false
panel.allowsMultipleSelection = false
let response = panel.runModal()
switch response {
case NSApplication.ModalResponse.OK:
openPanelDidSelectURL(panel.urls[0])
default:
return
}
}
(我记录了我的UI测试,以便立即接受NSOpenPanel
,选择打开它的文件夹。)
代码覆盖率最终像这样突出显示:
我尝试用switch
调用替换fatalError()
语句,但是UI测试仍然成功完成,提示在紧随其后的所有操作:
let response = panel.runModal()
在测试期间...不是 执行。
禁用沙箱似乎没有任何效果,因此我怀疑它以模态方式运行打开的面板会导致问题...
答案 0 :(得分:0)
我尝试了所有其他可用的方法来展示打开的面板,即:
panel.begin { (response) in
switch response {
case NSApplication.ModalResponse.OK:
self.openPanelDidSelectURL(panel.urls[0])
default:
return
}
}
...以及:
panel.beginSheetModal(for: view.window!) { (response) in
switch response {
case NSApplication.ModalResponse.OK:
self.openPanelDidSelectURL(panel.urls[0])
default:
return
}
}
...但结果始终相同:测试过程中未覆盖展示面板之后的所有代码。
最后,我意识到我的UI测试不能依赖于用户可以打开的面板位于任何位置的文件夹(上次访问的目录?),因此我选择使用 。
首先,在我的UI测试类中,我采用了以下设置逻辑:
override func setUp() {
continueAfterFailure = false
let app = XCUIApplication()
app.launchArguments.append("-Testing")
app.launch()
}
(“ Testing”之前的连字符是强制性的,否则我的基于文档的macOS应用会认为我正在启动它以打开名为“ Testing”的文档,但没有这样做) >
下一步,在应用程序端,我定义了一个全局计算属性来确定我们是否正在测试中运行
public var isTesting: Bool {
return ProcessInfo().arguments.contains("-Testing")
}
最后,也是在应用程序方面,我将所有NSOpenPanel
调用包装成两个方法:一个用于提示用户输入文件的读取,另一个用于提示用户输出将结果文件写入到的目录(这是我从NSOpenPanel
开始的所有应用程序需求):
public func promptImportInput(completionHandler: @escaping (([URL]) -> Void)) {
guard isTesting == false else {
/*
Always returns the URLs of the bundled resource files:
- 01@2x.png,
- 02@2x.png,
- 03@2x.png,
...
- 09@2x.png,
*/
let urls = (1 ... 9).compactMap { (index) -> URL? in
let fileName = String(format: "%02d", index) + "@2x"
return Bundle.main.url(forResource: fileName, withExtension: "png")
}
return completionHandler(urls)
}
// (The code below cannot be covered during automated testing)
let panel = NSOpenPanel()
panel.canChooseFiles = true
panel.canChooseDirectories = true
panel.canCreateDirectories = false
panel.allowsMultipleSelection = true
let response = panel.runModal()
switch response {
case NSApplication.ModalResponse.OK:
completionHandler(panel.urls)
default:
completionHandler([])
}
}
public func promptExportDestination(completionHandler: @escaping((URL?) -> Void)) {
guard isTesting == false else {
// Testing: write output to the temp directory
// (works even on sandboxed apps):
let tempPath = NSTemporaryDirectory()
return completionHandler(URL(fileURLWithPath: tempPath))
}
// (The code below cannot be covered during automated testing)
let panel = NSOpenPanel()
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.canCreateDirectories = true
panel.allowsMultipleSelection = false
let response = panel.runModal()
switch response {
case NSApplication.ModalResponse.OK:
completionHandler(panel.urls.first)
default:
completionHandler(nil)
}
}
这两个函数中使用实际NSOpenPanel
而不是模拟用户选择的文件/目录的部分仍被排除在收集代码覆盖率统计信息之外(但这一次,设计)。
但是至少现在只有这两个地方。我的其余代码仅调用这两个函数,不再直接与NSOpenPanel
进行交互。我已经从应用程序“抽象”了操作系统的文件浏览界面...