55

I've tried setting attributes in the XCUIApplication instance, in my UI Tests setUp()

let app = XCUIApplication()
app.launchEnvironment = ["testenv" : "testenvValue"]
app.launchArguments = ["anArgument"]
app.launch()

in didFinishLaunch I've tried to show these on screen when I run my UITests

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    if launchOptions != nil {
        for (key, value) in launchOptions! {  
            let alertView = UIAlertView(title: key.description, message: value.description, delegate: nil, cancelButtonTitle: "ok")
            alertView.show()
        }
    }

But I can't seem to be able to find the arguments and environment I set. Anyone know how to get a hold of them?

jarlh
  • 42,561
  • 8
  • 45
  • 63
bogen
  • 9,954
  • 9
  • 50
  • 89
  • Yea I can't find them either--wish there was more documentation on a lot of this stuff. It's cool just.. hard to get some good tests going. – cakes88 Oct 16 '15 at 15:06
  • Hi, do you by any chance know if any example of how to use ```launchArguments``` exists on apple web-site? The official documentation is just killing me: ```The arguments that will be passed to the application on launch``` (https://developer.apple.com/documentation/xctest/xcuiapplication/1500477-launcharguments). As if it's not obvious from name – olyv Dec 28 '17 at 08:36

9 Answers9

72

If you set launchArguments in a UI Test (Swift):

let app = XCUIApplication()
app.launchArguments.append("SNAPSHOT")
app.launch()

Then read them in your App using:

swift 2.x:

if NSProcessInfo.processInfo().arguments.contains("SNAPSHOT") {
   // Do snapshot setup
}

Swift 3.0

if ProcessInfo.processInfo.arguments.contains("SNAPSHOT") {
}

To set environment variables, use launchEnvironment and NSProcessInfo.processInfo().environment, respectively, instead.

Luca Davanzo
  • 21,000
  • 15
  • 120
  • 146
Joey C.
  • 2,168
  • 2
  • 16
  • 14
  • 6
    Under Xcode 7.3 this does not seem to be working. I've tried set launchArguments and then read them straight back. I always get back and empty array. Even if I do it on the very next line. Looks like they may be broken. Same for launchEnvironment. – drekka Apr 12 '16 at 01:15
  • 1
    Make sure you have only one XCUIApplication instance and call launch *after* setting launchArguments. – EricS May 14 '16 at 23:25
  • 4
    When `Record UI Test` in Xcode, it doesn't run `setUp()`. It misleads me the above codes not working. But run the UI test again (not `Record UI Test` mode), it works. – AechoLiu Dec 29 '17 at 03:03
  • 1
    In case any one else is banging their head as I was because it still wasn't working, the Build Configuration must also be set to Debug. If set to Release it will have empty launch arguments. – Nick Feb 06 '19 at 03:20
19

It's also interesting to note that arguments passed to XCUIApplication.launchArguments are also available from UserDefaults.

In your XCTestCase

let app = XCUIApplication()
app.launchArguments.append("-ToggleFeatureOne")
app.launchArguments.append("true")
app.launch()

In your target under test

UserDefaults.standard.bool(forKey: "ToggleFeatureOne") // returns true

From here you could create extensions on UserDefaults to provide handy run time toggles.

This is the same mechanism the Xcode schemes "arguments passed on launch" uses. Launch arguments and their effect on UserDefaults are documented under Preferences and Settings Programming Guide.

Jessedc
  • 12,320
  • 3
  • 50
  • 63
17

Building on Joey C.'s answer, I wrote a small extension to avoid using raw strings in the app. This way you avoid any typo issue and get autocompletion.

extension NSProcessInfo {
    /**
     Used to recognized that UITestings are running and modify the app behavior accordingly

     Set with: XCUIApplication().launchArguments = [ "isUITesting" ]
     */
    var isUITesting: Bool {
        return arguments.contains("isUITesting")
    }

    /**
     Used to recognized that UITestings are taking snapshots and modify the app behavior accordingly

     Set with: XCUIApplication().launchArguments = [ "isTakingSnapshots" ]
     */
    var isTakingSnapshots: Bool {
        return arguments.contains("isTakingSnapshots")
    }
}

This way you can use

if NSProcessInfo.processInfo().isUITesting {
   // UITesting specific behavior,
   // like setting up CoreData with in memory store
}

Going further, the various arguments should probably go into an enum that could be reused in the UITest when setting the launchArguments.

Arnaud
  • 17,268
  • 9
  • 65
  • 83
11

Here is example with launchArguments and Objective-C:

if ([[NSProcessInfo processInfo].arguments containsObject:@"SNAPSHOT"]) {
        //do snapshot;
}

Swift:

    let arguments = ProcessInfo.processInfo.arguments
    if arguments.contains("SNAPSHOT") {
        //do snapshot
    }
AlekseiPetrovski
  • 1,016
  • 9
  • 17
7

For launch arguments, pass them as two separate arguments:

let app = XCUIApplication()  
app.launchArguments.append("-arg")  
app.launchArguments.append("val")  
app.launch()

Taken from here.

Avi Cohen
  • 3,102
  • 2
  • 25
  • 26
7

Bear in mind a few details. Firstly, XCUIAppliction is not a singleton, therefore, if you call XCUIApplication().arguments.append("myargument") and then you call XCUIApplication().launch(), it won't send the arguments. Check it here.

Second, if you modify the arguments after launching the app it won't work, it will send the new arguments to the next execution.

4

I'm only aware of how this works in Objective-C

NSDictionary *environment = [[NSProcessInfo processInfo] environment];
Ceetn
  • 2,728
  • 2
  • 25
  • 28
1

If you need pass the environment variables from your schema to XCUITes, modify the XCTestCase -> app.launchEnvironment object, on each test class of this way:

Swift 3

override func setUp(){
    app.launchEnvironment = ProcessInfo.processInfo.environment
}
Maetschl
  • 1,330
  • 14
  • 23
0

1- Create an extension of ProcessInfo

extension ProcessInfo {

    public enum LaunchArgument: String {
        case uiTestSampleData
        case uiTestStagingData
        case none
    }

    public var launchArgument: LaunchArgument {
    
        if arguments.contains(LaunchArgument.uiTestSampleData.rawValue) {
            return .uiTestSampleData
        }
    
        if arguments.contains(LaunchArgument.uiTestStagingData.rawValue) {
            return .uiTestStagingData
        }
    
        return .none
    }

}

2- Modify your data source init. In this case I am using Moya, so I modify the provider init. Instead of initing your rest api normally, in the init check the arguments and mock the responses for sameplData arguments.

public init(provider: MyProvider<MyStoreApi> = MyProvider<MyStoreApi>()) {
    
    switch ProcessInfo.processInfo.launchArgument {
        
    case .uiTestSampleData:
        let provider = MyProvider<MyStoreApi>(stubClosure: MoyaProvider.immediatelyStub)
        self.provider = provider
        
    case .none, .uiTestStagingData:
        self.provider = provider
    }

}

3- In UI test add the argument

app.launchArguments.append("uiTestSampleData")
alegelos
  • 2,308
  • 17
  • 26