Writing DRY XCUITest Tests With Base Classes
Learn how to make XCUITest tests more readable, scalable, maintainable, and reusable.
Join the DZone community and get the full member experience.
Join For FreeIn our previous post on setting up the XCUITest framework, we got up and running with a sample XCUITest with Xcode 10. Apple's XCUITest framework gives us the ability to record basic user journeys to get started with XCUITest, but the recorded tests are not scalable and reusable. We have to make an effort 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 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 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 a 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. 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 later tests
These two are the very obvious things that we must abstract to be reused later. As the project and test suites grow, however, there might be the need to abstract more common workflows in the future. While writing XCUITest tests, it's essential to configure our app in a state where we can run tests reliably. Apple has provided the mechanisms 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 when we want to start each test from a clean state using the launch argument
XCUIApplication().launchArguments = ["-StartFromCleanState", "YES"]
This is an example of a launch argument, but you can pass any arguments or environment created by your iOS developers. We can configure that in the base class, and we will also extend the base class to the 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.
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 withtestWelcomeMessage()
- We have to replace the superclass of the test from
XCTestCase
toXCUITestBase
so that we can use all the common workflows from the base class. - Replace the instances of
XCUIApplication()
toapp
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 tidier. 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 issues. You can see the test logs from the Debug Area of Xcode, which is usually opened with the 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 Yourself
The source code of this tutorial is available in 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 like so:
$ git clone https://github.com/Shashikant86/XCUITest101
$ cd XCUITest101
$ git checkout baseclass
$ open XCUITest101.xcodeproj/
Once the project is opened in Xcode 10, 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 duplication. However, there is still much improvement needed to make our tests scalable and truly reusable. We will apply Swift's best test pattern to our XCUITest in the upcoming posts. Stay tuned.
Published at DZone with permission of Shashikant Jagtap, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments