You may already be familiar with the concept of Unit Testing. However, usually, Unit Tests are done for the logic of the application, but not so much for the visual aspects and the user interaction with the application itself. Making sure the user can interact correctly with the app is as important as what the logic of the app itself does, if not more. So how do we test this?
There are some ways to achieve this in different languages, platforms, and technologies. However, if you are developing a native iOS application, there is a simple and effective way to do it. UI unit testing.
UI unit testing is a kind of unit tests that can interact with the app as a user would, tapping on different elements and triggering gestures to ensure the flow of the app is as expected.
So let’s get started with UI unit testing, by creating our first tests and learning how to run them.
Incorporate UI unit testing into your project
First, let’s start by adding the unit tests to our project. If you are creating a new project from scratch, this is really easy to do. Actually, you don’t even have to do anything! By default, both unit tests and UI unit tests are added to a project when it is created on Xcode. You will see during the creation process that the checkboxes corresponding to these tests are already marked:
If you wish to add UI unit testing for an existing project it is not much harder. Just go to File -> New -> Target. On this screen just look for “iOS UI Testing Bundle” and add it to your project. Doing so will add a new folder to your project where all UI tests will be included. Pretty simple!
Creating UI Unit Tests
UI Tests will usually be located in their own folder, which by default will be called “projectNameUITests”. Inside this folder, there should already be a file for UI tests. The class that is defined here uses XCTestCase, and should override both the “setUp” and “tearDown” functions. The first test file should come with some comments that explain what those are, but basically “setUp” is called before each test, and “tearDown” is called after each test. You use them for setting the state of the app where your tests should run and doing the cleanup afterward.
Now we come to the main part of UI Unit Test: the tests themselves. We will create a new function for each test we want to do. Note that all test functions should start with “test” since that is how they are recognized as test functions instead of just auxiliary functions.
To start with, it is recommended that you create a var to reference the app itself on the test class since this will be used many times to retrieve screen elements. This can be done as follows:
class TestExamplesUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
}
You may notice “continueAfterFailure” in the code above. It pretty much does what it says on the tin. It is a boolean value that determines if a test should continue after it fails (and find everything that fails inside that test), or if it should stop and continue with the other tests.
Now we need to create the tests themselves. As with Unit Tests in general, we will create a certain flow for the test to follow and assert that what should happen has happened. The main difference is that, instead of testing pure logic and assert the results, we will interact with screen elements and assert that the screen is updated accordingly.
Here is a really simple example of a UI Unit Test, in which we will tap a button that is on the first screen (identified as “nextButton”) which should navigate to a second screen, and later check that a label (identified as “nextScreenLabel”) is on screen now that the navigation has been performed.
func testNavigateToNextScreen() {
XCTAssertTrue(app.buttons[“nextButton”].exists)
XCTAssertFalse(app.staticTexts[“nextScreenLabel”].exists)
app.buttons[“nextButton”].tap()
XCTAssertTrue(app.staticTexts[“nextScreenLabel”].exists)
}
As you see, first we check that the button that we want to press exists. No purpose in continuing our test if there is no button to press, right? After that, we check that the label that should only be on the second screen does not currently exist (test should start on the first screen of the App). Then we tap the button and check again if the label is now present. If everything worked correctly, this test should succeed when we run it.
There are other events we can trigger, like gestures on the App itself. For example, to create a swipe event from the user in the app, we can call app.swipeLeft(). It is that simple!
And now let’s see how to run the test and check the results.
Running tests and checking screenshots
As you’d expect, this is not rocket science.
You just need to go to the tab shown in the image above on Xcode and you will see all tests you have created, UI or otherwise. Now hover over any of the tests (or the entire testing bundle) and a “run” icon will appear. Click on it, and there you go! Just a word of warning, UI tests take much longer to run than other Unit Tests since it has to compile the app an emulate it on real time as a user would. But that is a minor price to pay.
After all selected tests have run, you will get an icon with a tick or an x indicating which of them failed and which of them succeeded.
However, this is not where it ends. We can actually see exactly how the app looked in many steps along the way of each test. Just right click on the test you want to examine more carefully, and click on “Jump to report”. You will see something like the following:
Now you just need to click on the “eye” icon at the right of one of the steps that have it, and you will see a screenshot of the app exactly at that moment of the process. Normally screenshots are taken after each interaction, so you should be able to see everything you need.
And there you have it! Here are the basics of UI Unit Testing on iOS on Swift. You should be able to take this basic tutorial and adapt it to your needs with some minor digging to get the exact interactions you need. We also have an example on our GitHub linked down below of the test done above and one extra test using the swipe interaction. Feel free to check it!