In XCode (as of 14.3) there are two types of testing projects; enter image description here

My understanding of them is;

Unit Testing Bundle - great for White Box testing, can (optionally) access Host Application APIs.

UI Testing Bundle - great for Black Box testing, cannot access Host Application APIs.

I would like to use the fantastic screenshot APIs of XCUIScreenshot as part of my Unit Testing bundles, but cannot seem to get it to work.

A few things I've tried:

  1. define a XCUIApplication as part of the XCTestCase init;

    app = XCUIApplication() let screenshot = app.windows.firstMatch.screenshot()

This obviously works well in UI Testing Bundle tests, but when its not a UI Test, the XCUIApplication cannot be created no matter the scheme. This kind of makes sense, as in the UI tests, it expects to manage this thing completely.

  1. use XCUIScreen to create it

    let screenshot = XCUIScreen.main.screenshot()

For a similar reason this fails; I've tried this on device and in simulator, there error is:

Failed to get screenshot: Not authorized for performing UI testing actions.

My hunch is that the screenshot capability is really being triggered "outside the device", and this is just not possible, but haven't found any conclusive text to say so.

Anyone with insight one direction or the other?

Footnote; my workaround is to use a UI Testing bundle, then bake in the white box code i need into my app, and trigger it via command line arguments... but it all feels pretty hoop jumpy. I want to do this to generate App Store screenshots as well as test various parts of my UI, and testing via pure button clicks is also, not ideal :)

1

There are 1 best solutions below

1
Leszek Szary On

Perhaps you could just do something more or less like below, but I think ui tests are better place to make screenshots:

extension UIView {
    func makeSnapshot() -> UIImage? {
        let renderer = UIGraphicsImageRenderer(size: frame.size)
        return renderer.image { _ in drawHierarchy(in: bounds, afterScreenUpdates: true) }
    }
}

extension UIImage {
    func save(name: String) {
        if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent(name), let data = pngData() {
            print("saving png to \(url)")
            try? data.write(to: url)
        }
    }
}

final class MyTests: XCTestCase {
    func testMyViewController() {
        let vc = MyViewController()
        let window = UIWindow()
        window.rootViewController = vc
        window.makeKeyAndVisible()
        let image = window.makeSnapshot()
        image?.save(name: "test123.png")
    }
}