NSOpenPanel ломает тестирование пользовательского интерфейса на macOS - PullRequest
2 голосов
/ 20 марта 2019

Я использую Xcode для тестирования пользовательского интерфейса в изолированном MacOS-приложении, которое имеет право com.apple.security.files.user-selected.read-write (т. Е. Может получать доступ к файлам и папкам, явно выбранным пользователем через NSOpenPanel GUI).

Я заметил, что покрытие кода прекращается сразу после того, как открытая панель представлена ​​модально.Это мой код:

@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
    }
}

(я записал свои тесты пользовательского интерфейса, чтобы сразу принять NSOpenPanel, выбрав папку, в которой он был открыт.)

Покрытие кода выделяется следующим образом:

enter image description here

Я пытался заменить оператор switch на вызов fatalError(), ноТест пользовательского интерфейса все еще успешно завершается, предполагая, что что-либо сразу после:

let response = panel.runModal()

... не выполнено во время теста.

Отключение песочницы, кажется, не имеет никакого эффекта, так что я подозреваю, что модем работает с открытой панелью, что вызывает проблемы ...

1 Ответ

0 голосов
/ 25 марта 2019

Я испробовал все другие доступные способы представления открытой панели, а именно:

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
    }
}

... но результатвсегда одно и то же: Весь код сразу после представления панели не покрывается во время тестов.


В конце я понял, что мои тесты пользовательского интерфейса не могут полагаться на некоторыеВыбираемая пользователем папка присутствует везде, где находится открытая панель (последний посещенный каталог?), поэтому я выбрал вместо этого mocking .

Во-первых, в моих тестовых классах пользовательского интерфейса Я применил эту логику настройки:

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 напрямую.Я "абстрагировал" интерфейс просмотра файлов ОС от моего приложения ...

...