Testing UI in Xcode 8

Writing unit tests helps to make sure your application's model and controller code are working properly, but as apps get more and more complex views, it's becoming increasingly important to test the interface of our apps as well. In Xcode 7, Apple introduced a new UI testing feature that allows us to do just that.

How does UI testing work?

When building an app, we have a certain way that our logic is expected to function. Earlier, we expected that by passing an input string to our save function we would get a data representation of a new snippet on disk, so we wrote a test to ensure this was happening. This same idea can be applied to the actual user interface, instead of the logic behind it.

For example, in our app, we want to make sure that when the New button is tapped, the user is presented with an action sheet for them to choose an option from. We could write a unit test to check the button code, but that won't check to make sure that the button event is firing properly in the first place, and that we're getting the proper UI response. UI testing aims to fix these shortcomings by essentially letting us code a fake user who taps on the screen, and letting us check to see what happens.

Adding the UI testing target

Before we can start writing UI tests, we need to create a separate UI test target, which must be separate from the unit test target we made earlier. This can be accomplished from the same plus button at the bottom of the test navigator (Figure 13.18):

Adding the UI testing target

Figure 13.18: Creating a new UI test target from the test navigator

Make sure the new target settings are correct; check with Figure 13.19 before creating it:

Adding the UI testing target

Figure 13.19: The correct settings for the new UI test target

We now have our UI test target added to the project, and we've been given a testing class to write our tests in, named SnippetsUITests.swift. It starts off a bit bloated, so let's cut that down to something a bit more manageable before moving on:

import XCTest

class SnippetsUITests: XCTestCase {
       
    override func setUp() {
        super.setUp()
        continueAfterFailure = false
        XCUIApplication().launch()
    }
   
}

Using the UI recorder

Now that we've got a testing class set up, we begin the daunting task of programming our fake user to play with our app, or do we? As a part of Xcode's robust testing suite, Apple has included a feature called the UI recorder that is built right into our UI testing class.

As the name implies, it is quite literally a record button that allows us to show Xcode what we want it to do by doing it ourselves! It then watches, and translates our actions into code that allows our test to repeat our actions later. To get a better idea of how this all works, it's best to see it in action: let's make a test!

Earlier, we said that we would want to check to make sure that when we tap the new button, our action sheet is presented. That's what we'll test now. Create a new function for this test:

func testSnippetSelectionOnNewPress() {
   
}

Now comes the fun part; click inside the brackets of the function, and then look along the bottom bar of the editor window. You should see a little record button (the red circle) next to the blue breakpoint icon. If you click the record button, your app will be launched, and then Xcode will start tracking your input and translating it to code on the fly!

Using the UI recorder

Figure 13.20: The UI recorder button on the bottom bar

Try clicking around on your application screen to see what happens in Xcode. Watch as it creates code based on what you're doing. Once you've had your fill, click the record button again to stop recording. Now delete all of the junk code you created, run the recorder again, and this time, just click New and stop the recording. You should be left with something like this:

func testSnippetSelectionOnNewPress() {
    XCUIApplication().toolbars.buttons["New"].tap()
   
}

This is the code that Xcode generated for us when we clicked that button. As you can see, it's very readable, and after a bit of practice you could easily write these tests by hand! But for now, the UI recorder is an invaluable tool to get our tests up and running as quickly as possible.

So, now that we have tapped our button, we need to check to make sure that the action sheet was presented to the user. However, this presents an issue: UI changes don't happen instantaneously. We need a way to wait a little bit to see what happens. To do this, we'll create an expectation then wait for a set amount of time to see if expectations are met:

func testSnippetSelectionOnNewPress() {
    
    // reference to application
    let app = XCUIApplication()
    
    // create and set expectation
    let selectionAlert = app.staticTexts["Select a snippet type"]
    let exists = NSPredicate(format: "exists == true")
    expectation(for: exists, evaluatedWith: selectionAlert, handler: nil)
    
    // tap button and wait for expectation
    app.toolbars.buttons["New"].tap()
    waitForExpectations(timeout: 1, handler: nil)
}

First, we are getting a reference to the application. Next, we create an XCUIElementQuery for something with the text Select a snippet type (which is the title of our action sheet), and then we create an NSPredicate for checking if the exists property is true. Then, we set an expectation for the selectionAlert to have exists == true. To finish our test, we tap the new button, and then give the test one second to see if the expectation becomes true:

Using the UI recorder

Figure 13.21: Our final passing UI test

If you run UI tests, you'll see that the app is launched in the simulator, and the actions you performed are mimicked (buttons will be virtually tapped, etc.). There's still a lot for you to go out and learn about UI testing, but this short primer should give you enough of a footing to understand the basics and get your feet wet.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.5.68