XCode iOS UI testing

As Martin Flowler described in his article TestPyramid, the UI should be the highest (and not the only) layer of the tests.  They are a safety net and errors detected on this layer can point to missing test’s on a lower layer. Automated UI test’s tend to be brittle, because every change in the UI can result in failing test’s.

Since version 7 XCode supports UI tests, with test classes deriving from XCTestCase:

Here’s an example test:

class PersonalProductivityAssistantUITests : XCTestCase {
    
    var app = XCUIApplication()
    var toolbarAddActivityButton: XCUIElement?
    var activityInputField: XCUIElement?
    //...
    var timeLogSaveButton: XCUIElement?
    
    override func setUp() {
    	super.setUp()
    	
    	continueAfterFailure = false
     
    	app.launch()
    
    	activityInputField = app.textFields["textEditActivity"]
    	toolbarAddActivityButton = app.toolbars.buttons["Log Time"]
        // ...
    	timeLogSaveButton = app.navigationBars["Time Log"].buttons["Save"]
    }
    
    override func tearDown() {
    	super.tearDown()
    	
    	app.terminate()
    }
}
func testCanAddAndEditAndDeleteActivityFromTable() {
    //
    // Add
    //
    // Open the add time log view
    waitForElementToAppear(toolbarAddActivityButton!)
    toolbarAddActivityButton!.tap()
    toolbarAddActivityButton!.tap()
    
    // Type new time log informations
    waitForElementToAppear(activityInputField!)
    let initialActivityName = getActivityNameWithDateTime()
    typeActivityName(initialActivityName)
    labelActivity?.tap()
    
    buttonPickDateTimeFrom?.tap()
    setDatePickerValues(monthAndDay: "Aug 1", hour: "10", minute: "30", amPm: "AM")
    setDateTimeFromButton?.tap()
    
    buttonPickDateTimeUntil?.tap()
    setDatePickerValues(monthAndDay: "Aug 1", hour: "11", minute: "15", amPm: "AM")
    setDateTimeUntilButton?.tap()
    
    timeLogSaveButton?.tap()
    
    // Verify element has been added
    XCTAssert(getTableStaticTextElement(initialActivityName).exists)
    
    //
    // Edit
    //
    // tap the actiity to open the add/edit segue
    getTableStaticTextElement(initialActivityName).tap()
    
    let changedActivityName = "\(getActivityNameWithDateTime()) #test"
    waitForElementToAppear(activityInputField!)
    clearAndTypeActivityName(changedActivityName)
    
    timeLogSaveButton?.tap()
    
    // Verify element has been modifed
    XCTAssert(getTableStaticTextElement(changedActivityName).exists)
    
    //
    // Delete
    //
    // Swipe up until the new element ist visible
    doSwipeUpUntilTableStaticTextIsHittable(changedActivityName)
    // Swipe left and push delete button
    doDeleteTableRow(changedActivityName)
    
    // Verify the element has been deleted
    XCTAssert(!getTableStaticTextElement(changedActivityName).exists)
}

This is how it looks like, when it runs in the debugger:

You can find the full test in the latest version of my learning app PersonalProductivityAssistant @github.

Learn more about recording UI tests.