How do I keep the app open between UITests in Xcode
Asked Answered
V

4

10

I have a series of UITests I want to run as individual tests but I don't want to relaunch the app between each test. How can I launch the app and keep it open so it doesn't shutdown and restart between tests.

I tried putting XCUIApplication().launch() in init() but got an error.

Vargueno answered 15/12, 2015 at 15:47 Comment(1)
I'm not sure if it's possible, but if it is, I suspect it should have something to do with the teardown() code. I don't know too much about it and a quick google search resulted in no real help. I hope you have better luck.Tensible
T
7

In your setUp() method, remove [[[XCUIApplication alloc] init] launch]; and put it into the first test you will be performing.

For eg.,

If you have tests: testUI(), testUIPart2(), testUIPart3(), etc., and it runs in this order, put [[[XCUIApplication alloc] init] launch]; into the first line of testUI() and no where else.

Tensible answered 16/12, 2015 at 13:53 Comment(1)
headdesk So obvious. I thought the app was closing do to some magic the tools not that it was closing down simply because it was relaunching.Vargueno
C
9

To improve on @James Goe's answer -

To avoid all those relaunches, but keep the ability to run individual tests - and not care about the order in which they're run - you can:

class MyTests: XCTestCase {
    static var launched = false

    override func setUp() {
        if (!MyTests.launched) {
            app.launch()
            MyTests.launched = true
        }

(Of course, now that you're not relaunching your app you have to care much more about state, but that's another story. It's certainly faster.)

Compressive answered 28/4, 2016 at 5:13 Comment(0)
T
7

In your setUp() method, remove [[[XCUIApplication alloc] init] launch]; and put it into the first test you will be performing.

For eg.,

If you have tests: testUI(), testUIPart2(), testUIPart3(), etc., and it runs in this order, put [[[XCUIApplication alloc] init] launch]; into the first line of testUI() and no where else.

Tensible answered 16/12, 2015 at 13:53 Comment(1)
headdesk So obvious. I thought the app was closing do to some magic the tools not that it was closing down simply because it was relaunching.Vargueno
A
2

This question is a bit old, but XCTestCase has a class func for setup() and tearDown() that executes before the first test, and after the last test completes. If you create your XCUIApplication() as a singleton (say XCUIAppManager), you could put your XCUIAppManager.shared.launchApp() call within the override class func setup() method... this will only launch the app once per XCTestCase (contains multiple tests)

override class func setUp() { // 1.
    super.setUp()
    // This is the setUp() class method.
    // It is called before the first test method begins.
    // Set up any overall initial state here.
}

https://developer.apple.com/documentation/xctest/xctestcase/understanding_setup_and_teardown_for_test_methods

Example app manager singleton implementation:

import XCTest

class XCUIAppManager {
    static let shared = XCUIAppManager()
    let app = XCUIApplication(bundleIdentifier: "com.something.yourAppBundleIdentifierHere")
    private(set) var isLaunched = false

    private init() {}

    func launchApp() {
        app.launch()
        isLaunched = true
    }
}
Aliaalias answered 5/10, 2018 at 17:58 Comment(3)
I think setUp method is called with every tests in a TestCase, tearDown is called when a test in TestCase finish?Apollinaire
The setUp() and tearDown() instance methods WILL be called for each test method. However, the class func setUp() and class func tearDown() class methods are only called once -> see the apple docs link in my answerAliaalias
I just tried this and the app still quits after the first test file completes when running XCUITests Xcode 11.3Revulsive
E
0

Here's another way to allow both common or non-common app launches on each test.

you can do it by creating your custom XCUIApplication and include state in your app launches as shown below.

import XCTest

class MyUIApplication: XCUIApplication {
    private(set) var isLaunched = false
    static let shared = MyUIApplication()

    private override init() {
        super.init()
        
        // add your launch arguments and environment keys
        launchArguments += ["isRunningTests"]
        launchEnvironment["state"] = "InitState"
    }

    func launchIfNeeded(at state: AppState) {
        if !isLaunched {
            launch(at: state)
        }
    }

    func launch(at state: AppState) {
        switch state {
        case .home:
            launchEnvironment["state"] = "InitState"
        ...
        }

        launch()
        isLaunched = true
    }
}

An example of test case with the above solution:

class MyViewControllerTests: XCTestCase {
    let app = VFGUIApplication.shared

    override func setUp() {
        super.setUp()

        app.launchIfNeeded(at: .home)
    }
}

This is preferable specially if you are working with a team and you want to make sure that launches are executed properly based on pre-defined launchArguments and launchEnvironment, etc.

Enchondroma answered 15/9, 2020 at 8:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.