Automatic iOS screenshots using XCTestplan and Xcode 11

There are several mobile applications in Raiffeisenbank that should work on a wide variety of devices and operating systems, so we try to automate the routine processes in testing. This article seemed useful to us, and we decided to translate it. foto source: unsplash.com If your application is multilingual, universal and designed for different devices, then you can spend a lot of time creating screenshots for each configuration. Imagine you have four languages, support for iPad and iPhone and you need to save 4 screens each - these are 32 screenshots. The process must be automated so as not to waste time every time the interfaces are updated.






The XCTestPlan tool, which appeared in Xcode 11, allows us to create several configurations for tests. Nowadays, configurations are most often used to determine how tests will run, including the choice of language for the application. In this article, you will learn how to use XCTestPlan to automate screenshots.

Launch XCTestplan


Let's set up a separate UITests target to automate the creation of screenshots, which is especially useful if you do not plan to run your main tests with these configurations. To create a new target, select File -> New -> Target and create a new UI Testing Bundle . After that, you need to add a new XCTestplan to this target. To do this, you can either use the menu Product -> Test Plan -> New Test Plan , or in the Scheme Editor select Convert to use Test Plans . If you do this through the menu, you need to make sure that the new target is on the `Test` tab.

Since we want to configure only the language settings, we can leave the Shared Settings unchanged and simply add a new configuration for each language that we support. This is what the settings for an application that supports English, German and French look like:

image

This is all the necessary setup and launch of XCTestplan.

Screenshot Automation


Your UI tests creating screenshots will practically not differ from your usual tests except that they will create attachments. You can also add XCTAssert checks so that the tests fail if something goes wrong.

Another important point: you may need to waitForExpectations in case your UI is not ready by the time the screenshot is taken ( keyboard showing / hiding, animation completion).

Below is an example with all the helper methods:

func testScreenshots() {
    let app = XCUIApplication()
    let searchButton = app.buttons["search"]
    searchButton.tap()
    
    let keyboard = app.keyboards.firstMatch
    waitForExistence(of: keyboard)
    app.typeText("Cupertino")
    add(takePromoShot(name: "Search"))
    
    let firstResult = app.cells.firstMatch
    firstResult.tap()
    waitForDisappearance(of: keyboard)
    add(takePromoShot(name: "Result"))
}

func waitForExistence(of element: XCUIElement) {
    let predicate = NSPredicate(format: "exists == TRUE")
    expectation(for: predicate, evaluatedWith: element, handler: nil)
    waitForExpectations(timeout: 5.0, handler: nil)
}

func waitForDisappearance(of element: XCUIElement) {
    let predicate = NSPredicate(format: "exists == FALSE")
    expectation(for: predicate, evaluatedWith: element, handler: nil)
    waitForExpectations(timeout: 5.0, handler: nil)
}

func takePromoShot(name: String) -> XCTAttachment {
    let lang = Locale.preferredLanguages[0]
    let screenshot = XCUIScreen.main.screenshot()
    let attachment = XCTAttachment(screenshot: screenshot)
    attachment.lifetime = .keepAlways
    attachment.name = "\(lang)-\(name)"
    return attachment
}

Pay attention to the latest feature. It takes the name of the snapshot, adds the language identifier, takes a screenshot and returns an XCTAttachment with the value of the .keepAlways parameter . This is important because by default, screenshots taken during tests are deleted if the tests succeed. Lifiteme is an important parameter because it ensures that the screenshot is saved after the test is completed. During UI tests, screenshots are constantly taken. They are very useful when you need to determine why and when the test fell; but by default the screenshots will be deleted if the tests were successful. Thus, when testScreenshots () succeeds, you will only have the necessary screenshots.

Habitual status bar


Most developers prefer to see on their screenshots a status bar with the right time, a complete network signal and other details. In Xcode 11, this is made even easier. There is a utility xcrun simctl status_bar , which allows you to specify these settings. This can be added as Build Phase :

xcrun simctl status_bar booted override --time 9:41 --operatorName ' ' --cellularMode active --cellularBar 4 --wifiBars 3 --batteryState charged

After this command, the status bar will look the same as in the marketing materials of Apple. Unfortunately, the team will not work on a real device, only in a simulator. It should also be noted that the simulator that you use must be running before you start the tests to collect screenshots.

Collecting the resulting screenshots


Now we can run our UI tests. When they pass, we will see the results in Report Navigator in Xcode. There will also be tests containing procedures grouped by language configurations. Procedures that have attachments are marked with a paper clip. Images themselves can be opened in QuickLook .

image

Separately collecting all the resulting pictures can be very time-consuming, especially if your application supports many languages. Unfortunately, these attachments do not appear as png or jpg among the test results (another change in Xcode 11). But you can parse the test results and find the JSON that points to the real attachments and collects them. This can be done using the xcparse utility.. It is installed using Homebrew and launched by the command:

xcparse screenshots /path/to/Test.xcresult /path/to/outputDirectory

Additional flags will group files by device, OS, or configuration. The names of our files contain a language identifier, so you can simply sort them by name.

Conclusion


The described approach has already allowed us to save enough time, especially in situations where there was a requirement to support the device with a new resolution or the interface was significantly updated. At WWDC20, I would like to see the choice of devices as part of the XCTestplan configuration, but for now, you have to choose manually or use a script that collects the necessary screenshots in the AppStore.

All Articles