Writing DRY XCUITest Tests With Base Classes

Writing Robust and Scalable XCUITest Tests with Base Classes

In our previous post on setting up XCUITest framework, we got up and running with a sample XCUITest with Xcode 10. Apple’s XCUITest framework gives us an ability to record the basic user journeys to get started with XCUITest, but the recorded tests are not scalable and reusable. We have to take efforts to make the XCUITest more readable, scalable, maintainable and reusable.

As per the approach mentioned in the previous post, we are able to add more UI tests but there will be a lot of duplication of code which could make our UI tests hard to maintain and fix. Writing automated tests is pretty easy but writing solid tests is harder as the size and complexity of the project grow incrementally. There are various testing approaches and test patterns available to make the tests scalable. In this post, we will cover how to make XCUITest tests robust by abstracting the common Swift code in the base class.

Create A Base Class for XCUITest Tests 

Most of the testing frameworks use the concept of the base class to abstract the common functionality of test classes. In short, the base class is the superclass which offers the common functionality to test classes. In our XCUITest101 Xcode project, we have a UI test class XCUITest101UITests.swift with all the methods like the setup, teardown and test methods in a single class. If we want to add new XCUITest tests, then we have to repeat the setup and teardown methods per test class. Clearly, this will create a lot of duplicate code in our iOS project which makes it hard to maintain later on and might prolong your test execution on a mobile device cloud. In order to avoid this, let’s create a base UI Test class from Xcode –> File –> New –> File –> Swift File –> Next and name the file XCUITestBase and make sure we have selected the target XCUITest101UITest for this file. 

This will create a new file XCUITestBase.swift under the UI test target where we can abstract the common code.

Abstract the Common Testing Workflow 

Now that, we have created the base class for XCUITest, we can think about the common and repetitive code that we can include in the base class for iOS device testing. Looking at our existing test class XCUITest101UITests, we can abstract following things in the base class:

  • Setup and Teardown methods
  • The instance of XCUIApplication() that might be needed in the later tests 

These two are the very obvious things that we must abstract so that it can be reused later. As the project and test suits grow, however, there might be the need to abstract the more common workflows in the future. While writing XCUITest tests, it’s essential to configure our app in the state where we can do the tests reliably. Apple has provided the mechanism of launch arguments and launch environments to be passed to each test class. It makes sense to configure an ability to add launch arguments from the base class. One good example would be, we want to start each test from the clean state using the launch argument

 XCUIApplication().launchArguments = ["-StartFromCleanState", "YES"]

This is an example of the launch argument but you can pass any arguments or environments created by your iOS developers.  We can configure that in the base class and we will also extend the base class to XCTestCase class. In the end, our XCUITestBase class looks like this

Now that we have created our base class, the next task is to modify our test XCUITest101UITest to the subclass of the base class and use common methods from the superclass.

Did you know Bitbar offers a mobile device cloud for automated and manual testing?

Sign up for a 30-day trial. No credit card required.

Refactor UI Tests to Use Base Class 

In order to refactor our original XCUITest101UITest we can take a few actions as mentioned below 

  • We don’t need the recorded test. So first let’s get rid of the testRecorded() test from our test class.
  • Next, we can rename our test method testRefactored() with something more sensible. As this is verifying the welcome message, let’s rename this test with testWelcomeMessage() 
  • We have to replace superclass of the test from XCTestCase to XCUITestBase so that we can use all the common workflows from the base class. 
  • Replace the instances of XCUIApplication() to app from the base class. 

Once we are done with the above steps, our test class will look like this

Now that our test class looks much better and tidy. We cut down our source code of 30+ lines to just 8 lines by using the base class. If you run this test by using the CMD+U key, you will see that our test is still passing without any issue. You can see the test logs from the Debug Area of Xcode which is usually opened by CMD+SHIFT+C (⇧⌘C) key in Xcode 10. In the case of our tests, logs look like this when the test is running in the simulators.

In the end, you can see that test will assert the welcome message and pass. 

Try It Your self 

The source code of this tutorial is available on the Github XCUITest101 repository in the baseclass branch here. You can download the source code and run the tests in the Xcode 10 by yourself. From the command line, you can get the source code 

 $ git clone https://github.com/Shashikant86/XCUITest101
 $ cd XCUITest101
 $ git checkout baseclass
 $ open XCUITest101.xcodeproj/

Once the project gets opened in the Xcode 10, then press CMD+U to run the XCUITest. 

Conclusion 

In this post, we have abstracted the common code of XCUITest in the base class to avoid the duplication. However, there is still so much improvement needed to make our tests scalable and truly reusable. We will apply the Swift’s best test pattern to our XCUITest in the upcoming posts. Stay tuned. 


An Essential Guide to XCTest Framework for iOS App Testing

Get all essentials about XCTest framework and learn how to get started with it for cloud testing

Download