Mallow's Blog

An Introduction to UI TESTING in iOS

UI testing is a process of testing how the software interface interacts with a user. It involves checking the ease of access, intuitiveness in navigating, compatibility with all the devices and crashes. It is important to have UI testing at an earlier stage of development to sort out issues at its roots. As time progresses the product becomes more complex and so even a minor issue will take longer and a complex process to sort out and restricts the user from using freely. UI testing can be done in two ways namely Manual and Automated.

MANUAL TESTING

In Manual testing, the tester will perform the operations by checking how the application behaves. He checks whether the product conforms to the client requirement. This approach has its own advantages and disadvantages. Higher time consumption, the effort needed and quality of the tester defines the success and efficiency of the process.

AUTOMATED TESTING

In Automated testing, the UI is tested with the help of a code. We can automate the repetitive tasks with the help of a code. By this, we can make testing after Refactoring easier.  In Xcode, XCTest framework and Accessibility tool are integrated which can be used for UI Testing. UI testing works by finding UI object using queries, creates events and sending to objects. We can compare the objects state and properties with the expected state. It is based 3 classes namely XUIApplication, XUIElement and XUIElementQuery. Below we are going to see a sample project for UI testing.

SETTING THINGS

We can set up UI test while creating a project by clicking Include Unit Tests checkbox or otherwise using File > New > Target and selecting iOS UI Testing Bundle.

SET UP PROJECT

In our project, we are having a nameTextField, showClueButton and travelToSpaceButtton. Initially, travelToSpaceButtton is hidden and showClueButton is not enabled.

    @IBOutlet weak var travelToSpaceButton: UIButton!
    @IBOutlet weak var showClueButton: UIButton!
    @IBOutlet weak var nameTextField: UITextField!
    
    // MARK: - View life cycle method
    
    override func viewDidLoad() {
        super.viewDidLoad()
        travelToSpaceButton.isHidden = true
        showClueButton.isEnabled = false
    }
    
    // MARK: - IBAction methods
    
    @IBAction func showClue(_ sender: UIButton) {
        travelToSpaceButton.isHidden = false
        sender.isHidden = true
    }
    
    @IBAction func travelToSpace(_ sender: UIButton) {
        performSegue(withIdentifier: "ShowSpaceViewController", sender: nil)
    }

TESTING FLOW

We are enabling showClueButton when a user enters text using text field delegate method. Once the user taps showClueButton we are making travelToSpaceButtton visible.

When the user taps the travelToSpaceButtton we are navigating to next Space Journey screen. The user clicks the start button in the navigation bar to get back to Start screen.

    // MARK: - Text field delegate method
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if let name = textField.text, name.count > 0 {
            showClueButton.isEnabled = true
        } else {
            showClueButton.isEnabled = false
        }
        return true
    }

WRITING THE FIRST UI TEST

After setting up a test target we will have a testing template with setUp and tearDown methods. SetUp method gets called before every test cases get started and tearDown method gets called after every test gets completed. Testing methods should start with prefix “test”. XCUIApplication returns the proxy for the application that we set as a target in our project. XCUIElement is the objects encapsulated with information used in UI. XCUIElementQuery is used to query the XCUIElement in the interface.

Using XCUIApplication we can access our app then by using XCUIElement we can create events and send to objects so that we can compare the expected output using XCTAssert class.

    var app: XCUIApplication!
    var nameTextField: XCUIElement!
    var buttons: XCUIElementQuery!
    var showClueButton: XCUIElement!
    var travelToSpaceButton: XCUIElement!
    
    override func setUp() {
        super.setUp()
        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false
        
        // Setting up the orientaion
        XCUIDevice.shared.orientation = .faceUp
        
        app = XCUIApplication()
        nameTextField = app.textFields["name"]
        buttons = app.buttons
        showClueButton = buttons["Show Clue"]
        travelToSpaceButton = buttons["Travel To Space"]
        
        // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
        app.launch()
    }
    
    override func tearDown() {
        nameTextField = nil
        buttons = nil
        showClueButton = nil
        travelToSpaceButton = nil
        app = nil
        super.tearDown()
    }
    
    // Testing UI by writing tests
    func testSucessSpaceJourney() {
        // We can test button state using XCTAssert
        XCTAssert(nameTextField.identifier == "name")
        XCTAssertEqual(showClueButton.isEnabled, false)
        
        // After tapping key board appears to type in text field
        nameTextField.tap()
        nameTextField.typeText("Mallow Tech")
        
        // Tap show clue button to make travelToSpaceButton visible
        showClueButton.tap()
        XCTAssert(travelToSpaceButton.isHittable == true)
        
        // Tap travelToSpaceButton to navigate to Space Journey screen
        travelToSpaceButton.tap()
        
        let labelText: String = "Wecome To Space"
        XCTAssertEqual(app.staticTexts["Wecome To Space"].label, labelText)
        app.navigationBars["Space Journey"].buttons["Start"].tap()
    }

WRITING UI TEST USING UI RECORDING

Keep the cursor inside a test method of the test target. Click on the start record option in the debug tab bar. Perform actions in the app which will get recorded. Once actions get recorded we can add assert for expected output in the test code written by XCode.

    // Testing UI using UI recording
    func testUIRecording() {
        XCUIDevice.shared.orientation = .faceUp
        let app = XCUIApplication()
        let nameTextField = app.textFields["name"]
        
        // We can test button state using XCTAssert
        XCTAssertEqual(app.buttons["Show Clue"].isEnabled, false)

        // After tapping key board appears to type in text field
        nameTextField.tap()
        nameTextField.typeText("mallow")
        
        // Tap show clue button to make travelToSpaceButton visible
        app.buttons["Show Clue"].tap()
        XCTAssert(app.buttons["Travel To Space"].isHittable == true)
        
        app.buttons["Travel To Space"].tap()
        app.navigationBars["Space Journey"].buttons["Start"].tap()
    }

SUCCESS AND FAILURE TESTS

In testing, we should write the tests in user’s perspective. We are enabling the showClueButton once nameTextfield got text. Here we are going to write a successful test case that tests the showClueButton’s state.

    func testShowClueButtonSucessState() {
        // After tapping key board appears to type in text field
        nameTextField.tap()
        nameTextField.typeText("Mallow Tech")
        
        // showClueButton is enabled once we have text in the text field
        XCTAssertEqual(showClueButton.isEnabled, true)
    }

We are navigating to space journey screen when a user taps travelToSpaceButton. For testing failure case, we are testing with the start screen navigation bar after we reached the Space Journey screen. Use Cmd + U for running test.

func testFailureSpaceJourney() {
        // After tapping key board appears to type in text field
        nameTextField.tap()
        nameTextField.typeText("Mallow Tech")
        showClueButton.tap()
        travelToSpaceButton.tap()
        
        // Currently we are in Space Journey screen
        XCTAssert(app.navigationBars["Start"].exists)
    }

CONCLUSION

In this post, we have seen the UI testing methods. With a sample project, we saw in detail about Automated testing. In a future blog, we will see about Unit Testing.

 

Srikanth T,
iOS Development Team,
Mallow Technologies.

Leave a Comment

Your email address will not be published. Required fields are marked *