Xcode UI Testing with Swift

Hi friends, hope you all have implemented several mobile apps so far. But when you need to add new features or fixing bugs, you need to keep in mind that not to break existing functionality. To do that, we write test cases. Today I’m going to tell you about how to write UI test cases with Swift programing language for iOS applications. ūüôā

I believe you are aware how to create a UI test target on your Xcode project. Once you do that, you will get a default Swift class with example test case. In that class there will be three functions already declared. Those are;

override func setUp()

override func tearDown()

func testExample()

setUp() function is called before the invocation of each test function in the class. tearDown() is called after the invocation of each test method in the class. If we run the whole test scenario at this point, first it will call setUp() function, then the testExample() and lastly tearDown() function.

One of the important thing that you may need to do is, your application source code needs to figure out whether it is run as a normal process or UITesting process. One of the practical reason you need that feature is, assume you have some values store in the shared preference and you need to clear them before running each test case written in that test class. What you have to do is, pass launch arguments like below.

    override func setUp() {
        super.setUp()

        continueAfterFailure = false

        let app = XCUIApplication()
        app.launchArguments.append("Testing-UI")
        app.launch()
    }

Then you have to check it on your application source like this.

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

        if ProcessInfo.processInfo.arguments.contains("Testing-UI") {

            value = 1
        }

        print("# value = ", value)

        return true
}

Just for testing purposes I put an int variable which gives 0 when you run the application normally and gives 1 when you run the UI test case. You can see it in Xcode debug area.

Once you ready to write a test case what you have to do is using the UI test recording functionality. You write a function inside a class which is a subclass of XCTestCase. Function name should start with “test” prefix. As an example valid function name is testLoginProcess. If you have a concern about the execution order of the functions written inside a class write those in order that you want to run. Check below example

func testCancelationOfIdSrvr() { }
func testUsernamePasswordLogin() { }

When test scenario is running testCancelationOfIdSrvr() function is executing first and then the testUsernamePasswordLogin(). Likewise if you want to execute the test classes of your desired order, simple solution is naming the classes with alphabetical order. To maintain a meaningful readable format I did something like below.

Xcode test navigator

Test classes naming

What I have done was add a prefix that can increment and the class name suffix contain the meaningful name which represent the content of the class.

Another useful tip when writing test cases is having a XCUIApplication extension. Practical scenario is responding to Alert while testing. Most commonly, Alert is having two user actions which are pressing OK or Cancel and that Alert could be used in several test classes. In such a case it is better if we have a common code where we can handle the Alert action. What I did was something like below.

import XCTest

extension XCUIApplication {

    func pressOkIfAlert() {

        let alerts = self.alerts;

        if (alerts.count > 0) {
            alerts.element(boundBy: 0).buttons["OK"].tap();
        }
    }

    func pressCancelIfAlert() {

        let alerts = self.alerts;

        if (alerts.count > 0) {

            alerts.element(boundBy: 0).buttons["Cancel"].tap();
        }
    }
}

Then, in my test case, I can call those functions where it is necessary. Such as;

    func testCancelationOfIdSrvr() {

        let app = XCUIApplication(bundleIdentifier: "com.company.product");

        app.otherElements.containing(.image, identifier:"LoginViewBackground").children(matching: .button).element.tap()

        app.pressCancelIfAlert()

        // Rest of the code
    }

Another thing that you may miss once you record a test session and replay it, is waiting time until some UI element appears. There are two ways that we can delay the process. One is asking process to sleep for a certain amount of seconds and the other is wait for certain time until some UI element appears.  Example for sleep is;

        app.pressCancelIfAlert()
        sleep(7)
        app.navigationBars["Authenticate"].buttons["Cancel"].tap()

Example for wait until an UI element appears is;

        let passwordField = // Initialize your password field here
        _ = usernameTextField.waitForExistence(timeout: 6)
        usernameTextField.tap()

Since this post is getting lengthier, I will conclude this post here. Hopefully we will meet in another UITestCase related post. Have a good testing session ūüôā

 

 

 

 

Advertisements

About AnujAroshA

Working as an Associate Technical Lead. Specialized in iOS application development. A simple person :)
This entry was posted in iOS and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s