23
votes

Apple introduced in Xcode 7 new UI Testing but I have a struggle whenever the tests launches the app, it starts with data that the application had before. It means tests cannot be independent and can be influenced by other tests.

It is not possible to access user defaults and other data because the application that is running tests has no access to the bundle of the tested application. Scripts are also out of question because they can be run before or after testing. And there is no way how to execute NSTask on iOS to run a script before each test suite.

Is there a way how do reset the application data before each test suite?

4

4 Answers

21
votes

Not in a straight forward manner. But there are some workarounds.

The XCUIApplication can set command line arguments and environment variables that can alter your application’s behavior.

A simple example of your main.m file:

int main(int argc, char * argv[]) {
#if DEBUG
    // Reset all data for UI Testing
    @autoreleasepool {
        for (int i = 1; i < argc; ++i) {
            if (0 == strcmp("--reset-container", argv[i])) {
                NSArray *folders = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
                NSFileManager *fm = [[NSFileManager alloc] init];
                for (NSString *path in folders) {
                    [fm removeItemAtPath:path error:nil];
                }
                // Also remove documents folder if necessary...
            }
        }
    }
#endif
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil,
                                 NSStringFromClass([AppDelegate class]));
    }
}

And in -[XCTestCase setUp] add:

XCUIApplication *app = [[XCUIApplication alloc] init];
app.launchArguments = @[@"--reset-container"];
[app launch];
6
votes

If preparing the app for UITests inside application:didFinishLaunchingWithOptions: is ok in your case, then you can do the following:

In setUp() method of your test class extending XCTestCase add following code:

let application = XCUIApplication()
application.launchEnvironment = ["UITESTS":"1"]
application.launch()

Then, in application:didFinishLaunchingWithOptions: you can check for the flag using following code:

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {

    let env = ProcessInfo.processInfo.environment
    if let uiTests = env["UITESTS"], uiTests == "1" {
        // do anything you want
    }
    // further set up code
}

Of course if that is an option for you.

NOTE: Instead of setting "1" as argument for "UITESTS" flag, you can specify different values for different test cases - or even test methods (but in such case, you should launch the application from test method, not setUp())

NOTE 2: I suggest wrapping the code dealing with the flag into #if DEBUG block.

5
votes

I got to reset the application data using some private headers to access to the springboard and the settings app.

First I added a Run script phase to remove it when the tests starts:

/usr/bin/xcrun simctl uninstall booted com.mycompany.bundleId

And after that I use the solution I wrote here to remove it using a test script that runs on the tearDown calls to reset it after every test.

0
votes

In my case I required to also reset permissions. And there's an option to delete your app and reset system permissions just making the test delete the app and navigate to settings.

Already answered in this S.O. thread: Is there a way to reset the app between tests in Swift XCTest UI in Xcode 7?