0

I have a class teardown which is trying to remove the app, but it doesn't recognize app.terminate().

class DeviceSettingsUtilities : UITestUtilities {
func removeApp(productName:String){
        print("in teardown")
        let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
        XCUIApplication().terminate() // this does nothing
        XCUIApplication(bundleIdentifier: "com.xxx.xxxx").terminate()//this does nothing too, but this works when called as an instance teardown
        sleep(5)
        springboard.activate()
        let icon = springboard.icons.matching(identifier: productName).firstMatch
// icon.exists is false when called as a class teardown
// icon.exists is true when called as an instance teardown
        if icon.exists {
            let iconFrame = icon.frame
            let springboardFrame = springboard.frame
            icon.press(forDuration:1.3)
            springboard.coordinate(withNormalizedOffset: CGVector(dx: ((iconFrame.minX + 3) / springboardFrame.maxX), dy:((iconFrame.minY + 3) / springboardFrame.maxY))).tap()
            sleep(5)
            springboard.buttons["Delete"].firstMatch.tap()
            sleep(5)
        }
        XCUIApplication().terminate()
    }

}

This is being called in the test case class teardown method as shown below

override class func tearDown() {
    super.tearDown()
    let deviceSettings = DeviceSettingsUtilities()
    deviceSettings.removeApp(productName: ProductName.rawValue)
}

This just doesnt delete the app, But if i change class func tearDown() to func tearDown() , it deletes the app with no problem. Not sure what i am missing. Any suggestions ?

velapanur
  • 307
  • 3
  • 5
  • 19
  • What behaviour are you expecting/trying to get? – Oletha Nov 07 '18 at 18:20
  • @Oletha - I want the app to get uninstalled, So that when the next test runs, i can start off from scratch ie going through the sign up flow. Right now it doesnt uninstall the app, and the next time the test runs , and it expects the app to be in fresh state, it fails. At the same time , I dont want it to uninstall the app after every test, but more after the entire class runs – velapanur Nov 07 '18 at 19:49

3 Answers3

2

This seems like a bug in latest Xcode 10. XCUIApplication.terminate() doesn't seem to work in tearDown() when declared as class.

This can be solved in two ways:

The first option is to use:

override func tearDown() {
    XCUIApplication().terminate()
    super.tearDown()
}

instead of:

override class func tearDown() {…} 

Or, terminate the app differently (press home button, open different app...). However, I would use the first way.

Also consider reporting this to Apple, so they can fix it.

Edit: This has nothing to do with app state (XCUIApplication().state.rawValue), since it is same in test and in tearDown() (4 = running foreground). Also - official documentation says that .terminate() will terminate app, which has a debug session with Xcode, but the debug session is active in tearDown() as well. So it is really probably a bug in Xcode.

shim
  • 9,289
  • 12
  • 69
  • 108
Václav
  • 990
  • 1
  • 12
  • 28
1

The app is not being reset when you put the code in the class tearDown method because that method only runs once all the tests in the class are complete. The instance tearDown is the best place to put code that you want to run after every test.

From Apple's documentation:

For each class, testing starts by running the class setup method. For each test method, a new instance of the class is allocated and its instance setup method executed. After that it runs the test method, and after that the instance teardown method. This sequence repeats for all the test methods in the class. After the last test method teardown in the class has been run, Xcode executes the class teardown method and moves on to the next class. This sequence repeats until all the test methods in all test classes have been run.

Oletha
  • 7,324
  • 1
  • 26
  • 46
  • That is exactly what i want it to do. I want the app to be uninstalled after all the tests are run in the class, but that is not happening now. – velapanur Nov 07 '18 at 19:55
  • 1
    Apologies, I misunderstood. It seems that no interaction with XCUIApplication will work in class methods, I think this may be a limitation of the way Xcode UI tests work. – Oletha Nov 07 '18 at 19:57
  • oh no :( , but atleast i know now. Thank you @Oletha ! – velapanur Nov 07 '18 at 20:00
1

I'm using the below workaround to terminate the app after last test case of a class.

class BaseClass: XCTestCase {

        static var LastTestCaseName: String = ""

        override class func setUp() {
            LastTestCaseName = defaultTestSuite.tests.last!.name
            super.setUp()
        }

        override func tearDown() {
            let app = XCUIApplication()
            if BaseClass.LastTestCaseName == testRun?.test.name {
                app.terminate()
            }
        }
    }