Reputation: 2345
Recently I started testing an iOS app using XCTest but I found some difficulties, the main difficulty was deleting or resetting the app content in each test class.
I'm currently using XCode 11 and trying to delete/reset an app from iOS 13 for each test class, I've already tried:
This step is really important in my tests because in each test I need to create a profile and log in, so in the next test I need to have the app just installed from scratch
Upvotes: 12
Views: 7005
Reputation: 1431
you can simply do this via springboard same as you uninstall build by yourself. Here is the class with deleteApp function which will uninstall build at anytime you need.
class Springboard {
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
class func deleteApp () {
//terminate app and activate Springboard
XCUIApplication().terminate()
springboard.activate()
//tap on app icon
let appIcon = springboard.icons.matching(identifier: "App Display Name").firstMatch
if appIcon.exists {
appIcon.press(forDuration: 2.0)
//Access first alert button (Remove App)
let _ = springboard.alerts.buttons["Remove App"].waitForExistence(timeout: 1.0)
springboard.buttons["Remove App"].tap()
//Access second alert button (Delete App)
let _ = springboard.alerts.buttons["Delete App"].waitForExistence(timeout: 1.0)
springboard.buttons["Delete App"].tap()
//Access second alert button (Delete)
let _ = springboard.alerts.buttons["Delete"].waitForExistence(timeout: 1.0)
springboard.buttons["Delete"].tap()
}
}
}
Usgae:
func test_YourTestName() {
Springboard.deleteApp()
}
Upvotes: 1
Reputation: 587
iOS 14 and below
func deleteMyApp() {
XCUIApplication().terminate()
let bundleDisplayName = "App Name"
XCUIDevice.shared.press(.home)
let icon = springboard.icons[bundleDisplayName]
if icon.waitForExistence(timeout: 5) {
XCUIDevice.shared.press(.home)
let value = springboard.pageIndicators.element(boundBy: 0).value as? String
let f = value!.last?.wholeNumberValue
for _ in (1...f!){
if(icon.isHittable){
break
}
else{
springboard.swipeLeft()
}
}
let systemVersion = UIDevice.current.systemVersion.prefix(2)
switch systemVersion {
case "13":
icon.press(forDuration: 1)
let rrd = springboard.buttons["Delete App"]
XCTAssert(rrd.waitForExistence(timeout: 5))
rrd.tap()
let rrd2 = springboard.buttons["Delete"]
XCTAssert(rrd2.waitForExistence(timeout: 5))
rrd2.tap()
case "14":
icon.press(forDuration: 1)
let rrd = springboard.buttons["Remove App"]
XCTAssert(rrd.waitForExistence(timeout: 5))
rrd.tap()
let rrd2 = springboard.buttons["Delete App"]
XCTAssert(rrd2.waitForExistence(timeout: 5))
rrd2.tap()
let rrd3 = springboard.buttons["Delete"]
XCTAssert(rrd3.waitForExistence(timeout: 5))
rrd3.tap()
default:
XCTFail("Did not handle")
}
}}
Upvotes: -1
Reputation: 9452
The working solution for iOS 14
import XCTest
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
func deleteMyApp() {
XCUIApplication().terminate()
let bundleDisplayName = "MyApp"
let icon = springboard.icons[bundleDisplayName]
if icon.exists {
icon.press(forDuration: 1)
let buttonRemoveApp = springboard.buttons["Remove App"]
if buttonRemoveApp.waitForExistence(timeout: 5) {
buttonRemoveApp.tap()
} else {
XCTFail("Button \"Remove App\" not found")
}
let buttonDeleteApp = springboard.alerts.buttons["Delete App"]
if buttonDeleteApp.waitForExistence(timeout: 5) {
buttonDeleteApp.tap()
}
else {
XCTFail("Button \"Delete App\" not found")
}
let buttonDelete = springboard.alerts.buttons["Delete"]
if buttonDelete.waitForExistence(timeout: 5) {
buttonDelete.tap()
}
else {
XCTFail("Button \"Delete\" not found")
}
}
}
class HomeUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
deleteMyApp()
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}
Upvotes: 14
Reputation: 3104
This is a quick and dirty solution for iOS 14:
let appName = "You app name as it appears on the home screen"
// Put the app in the background
XCUIDevice.shared.press(XCUIDevice.Button.home)
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
if springboard.icons[appName].waitForExistence(timeout: 5) {
springboard.icons[appName].press(forDuration: 1.5);
}
if springboard.collectionViews.buttons["Remove App"].waitForExistence(timeout: 5) {
springboard.collectionViews.buttons["Remove App"].tap()
}
if springboard.alerts["Remove “\(appName)”?"].scrollViews.otherElements.buttons["Delete App"].waitForExistence(timeout: 5) {
springboard.alerts["Remove “\(appName)”?"].scrollViews.otherElements.buttons["Delete App"].tap()
}
if springboard.alerts["Delete “\(appName)”?"].scrollViews.otherElements.buttons["Delete"].waitForExistence(timeout: 5) {
springboard.alerts["Delete “\(appName)”?"].scrollViews.otherElements.buttons["Delete"].tap()
}
Upvotes: 0
Reputation: 21
Updated Roman's answer to support iOS 13 on iPad.
By inserting otherElements["Home screen icons"]
, the code works both of iPhone and iPad.
class Springboard {
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
class func deleteMyApp() {
XCUIApplication().terminate()
// Insert otherElements["Home screen icons"] here
let icon = springboard.otherElements["Home screen icons"].icons["Workoutimer"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.press(forDuration: 5)
// 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()
XCUIDevice.shared.press(XCUIDevice.Button.home)
}
}
}
On iPad, "recent launched app" will be added to the right side of the Dock. This iPad specific behavior leads XCUITest finds two elements if you search icons only by its identifier.
Your app will be added here, too.
To handle that, specify otherElements["Home screen icons"]
and it excludes elements of the "Dock".
Upvotes: 2
Reputation: 119
enum Springboard {
static let springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")
static let appName = "appName"
static func deleteApp() {
XCUIApplication().terminate()
let icon = springboardApp.icons[appName]
if icon.exists {
icon.press(forDuration: 3)
icon.buttons["DeleteButton"].tap()
springboardApp.alerts.buttons["Delete"].tap()
}
}
}
Upvotes: 1
Reputation: 2273
Try to press the app icon a little longer than in previous iOS versions.
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
func deleteMyApp() {
XCUIApplication().terminate()
let icon = springboard.icons["YourAppName"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.press(forDuration: 5)
// 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()
}
}
Upvotes: 3