Is there an API call within XCTest that I can put into the setUP() or tearDown() to reset the app between tests? I looked in the dot syntax of XCUIApplication and all I saw was the .launch()
OR is there a way to call a shell script in Swift? I could then call xcrun in-between test methods to reset the simulator.
Tests are run directly from Xcode and are written with either Swift or Objective C. XCUITest makes use of the application's accessibility functionality, which allows tests to interact with the app as a real user would.
Use the XCTest framework to write unit tests for your Xcode projects that integrate seamlessly with Xcode's testing workflow.
To test SwiftUI applications, don't test SwiftUI code. The app should say "Hello, <user> !" when given a user and fallback to "Hello, world!" otherwise.
You can add a "Run Script" phase to build phases in your test target to uninstall the app before running unit tests against it, unfortunately this is not between test cases, though.
/usr/bin/xcrun simctl uninstall booted com.mycompany.bundleId
Update
Between tests, you can delete the app via the Springboard in the tearDown phase. Although, this does require use of a private header from XCTest. (Header dump is available from Facebook's WebDriverAgent here.)
Here is some sample code from a Springboard class to delete an app from Springboard via tap and hold:
#Swift 4:
import XCTest class Springboard { static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") /** Terminate and delete the app via springboard */ class func deleteMyApp() { XCUIApplication().terminate() // Force delete the app from the springboard let icon = springboard.icons["Citizen"] if icon.exists { 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() } } }
#Swift 3-:
import XCTest class Springboard { static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard") /** Terminate and delete the app via springboard */ class func deleteMyApp() { XCUIApplication().terminate() // Resolve the query for the springboard rather than launching it springboard.resolve() // Force delete the app from the springboard let icon = springboard.icons["MyAppName"] if icon.exists { let iconFrame = icon.frame let springboardFrame = springboard.frame icon.pressForDuration(1.3) if #available(iOS 13.0, *) { springboard.buttons["Remove App"].tap() springboard.alerts.buttons["Delete App"].tap() springboard.alerts.buttons["Delete"].tap() } else { // Tap the little "X" button at approximately where it is. The X is not exposed directly let xPosition = CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX, dy: (iconFrame.minY + 3) / springboardFrame.maxY) springboard.coordinate(withNormalizedOffset: xPosition).tap() springboard.alerts.buttons["Delete"].tap() } } } }
And then:
override func tearDown() { Springboard.deleteMyApp() super.tearDown() }
The private headers were imported in the Swift bridging header. You'll need to import:
// Private headers from XCTest #import "XCUIApplication.h" #import "XCUIElement.h"
Note: As of Xcode 10, XCUIApplication(bundleIdentifier:)
is now exposed by Apple, and the private headers are no longer needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With