1

I'm writing a framework around XCUITest that adds all kinds of convenient features (such as local HTML reports, Testrail integration, etc.) and I found that for our purpose it makes sense to have one class that extends XCTestCase and run all our tests from that (instead of having every test case class extend from XCTestCase and launch it via schemes which is very awkward). Our actual test classes (called Features) are then started sequentially from the one XCUITest method invocation.

This all works well to the point when I want to delete and re-install the tested app in between test cases.

I'm using the class from Is there a way to reset the app between tests in Swift XCTest UI in Xcode 7? to control the springboard via XCUITest.

The app is re-installed before every test case and it succeeds before the first test but then before the second test case I always get an error and the test runner quits:

t =    47.84s         Find the Application "com.apple.springboard" 0x6080000ac2a0 (retry 2)
t =    47.84s             Snapshot accessibility hierarchy for com.apple.springboard
t =    47.90s         Assertion Failure: Springboard.swift:46: (null)

The error happens when resolve() is called a second time:

// Resolve the query for the springboard rather than launching it
springboard.resolve()

My related framework method which deletes and reinstalls the app:

func reinstallApp()
{
    Springboard.deleteApp()
    app.launchArguments.append("--uitesting")
    app.launch()
}

Does anyone know a workaround that would prevent this error?

Update for Xcode 9:

I was hoping to fix the issue with Xcode 9 and Swift 4 and the new XCUIApplication.activate() method. My Springboard proxy class looks like this:

class Springboard
{
    static let springboard:XCUIApplication? = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    static let settings:XCUIApplication? = XCUIApplication(bundleIdentifier: "com.apple.Preferences")

    class func deleteApp()
    {
        XCUIApplication().terminate()

        if let springboard = springboard
        {
            springboard.activate()

            /* Force delete the app from the springboard. */
            let icon = springboard.icons["appname"]
            if icon.isHittable
            {
                let iconFrame = icon.frame
                let springboardFrame = springboard.frame
                icon.press(forDuration: 1.3)

                /* Tap the little "X" button at approximately where it is. The X is not exposed directly. */
                springboard.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX, dy: (iconFrame.minY + 3) / springboardFrame.maxY)).tap()
                springboard.alerts.buttons["Delete"].tap()

                /* Press home once to make the icons stop wiggling. */
                XCUIDevice.shared.press(.home)
                /* Press home again to go to the first page of the springboard. */
                XCUIDevice.shared.press(.home)
                /* Wait some time for the animation to end. */
                Thread.sleep(forTimeInterval: 0.5)

                if let settings = settings
                {
                    let settingsIcon = springboard.icons["Settings"]
                    if settingsIcon.isHittable
                    {
                        settingsIcon.tap()
                        settings.tables.staticTexts["General"].tap()
                        settings.tables.staticTexts["Reset"].tap()
                        settings.tables.staticTexts["Reset Location & Privacy"].tap()
                        settings.buttons["Reset Warnings"].tap()
                        settings.terminate()
                    }
                    else
                    {
                        XCUIDevice.shared.press(.home)
                        let settingsIcon = springboard.icons["Settings"]
                        if settingsIcon.isHittable
                        {
                            settingsIcon.tap()
                            settings.tables.staticTexts["General"].tap()
                            settings.tables.staticTexts["Reset"].tap()
                            settings.tables.staticTexts["Reset Location & Privacy"].tap()
                            settings.buttons["Reset Warnings"].tap()
                            settings.terminate()
                        }
                    }
                }
                else
                {
                    print("iOS Settings app not found!")
                }
            }
            else
            {
                print("App icon not found!")
            }
        }
        else
        {
            print("Springboard not found!")
        }
    }
}

But now I'm getting the following error:

Non-critical error encountered: Automatic screenshot failure.
Linked XCTest.framework from /Users/user/Library/Developer/CoreSimulator/Devices/B9AC848C-37C8-46D6-8322-ED78E7201CFE/data/Containers/Bundle/Application/5AB3F814-9DF8-4F1C-BC46-478C42951E60/UITests-Runner.app/Frameworks/XCTest.framework/XCTest, built with Xcode 0900(9A221a), modified on Thursday, September 28, 2017 at 3:10:06 PM Japan Standard Time
XCTest.framework bundle version: 13201
Executing on OS: Version 11.0 (Build 15A372)
Screenshot request returned nil with no additional error information.
Please file a bug and attach the details above.

Does anyone know how to solve this new issue?

BadmintonCat
  • 9,416
  • 14
  • 78
  • 129

0 Answers0