BDD for XCUITest with Swift Protocols and Extensions

  October 16, 2018

In the 3rd blog of XCUITest 101 series, we’ll cover something about Swift Protocols and Extensions. In the last post on DRY XCUITest with Base classes, we have abstracted our code in the base classes in order to avoid the duplication of the code. We have achieved this using the object-oriented inheritance approach. However, Swift is a protocol-oriented language, and we will see how we can use Swift protocols and extension to make our XCUITests more human readable. We will apply Behaviour-Driven Development a.k.a BDD in the methodology for our XCUITest.

BDD in Swift

The concept of  BDD has widely applied with Cucumber in Ruby and many other languages but BDD in Swift was always challenging. It’s because of lack of proper BDD tools like Cucumber for Ruby, SpecfFow for .NET or Behat for PHP. There are libraries like XCTest-Gherkin and Cucumberish which are available to achieve BDD for iOS projects but they are not as powerful as from other languages. It’s still possible to make great use of native Swift features like protocols, extensions and enumerations while writing acceptance tests in BDD style. We can use Swift’s protocol-oriented approach to write BDD style tests by capturing intended behavior in the form of Swift Protocol which is similar to the Features format in Gherkin. We can then think of all possible scenarios in the form of XCTest test methods which is similar to scenario titles in the Gherkin. We can also write steps in the form of methods in Given/When/Then a.k. a GWT using Swift extensions.

No time to read the blog? Take away this free XCUITest 101 ebook

XCUITest BDD in Action

In our XCUITest101 app, we have a test already written to verify the welcome message. Now we will add another test in BDD or human-readable format like this:

func testWelcomeMessageInBDDStyle() {
          givenILaunchedAnApp()
          whenITapOnEnter()
          thenIShouldSeeWelcomeMessage()
}

At this point, Xcode will give all the red errors use of unresolved Identifier givenILaunchedAnApp, which means we have to implement the steps to make it pass and do actions or assertions. Now, we will create another file inside the XCUITest101UITest target and call it as WelcomeStepDefinitions.swift shown below.

XCUITest101UITest

We have to implement all these steps in the Swift extension, and we can write an extension on top of the XCUITestBase class as shown below

extension XCUITestBase {
     func givenILaunchedAnApp() {
          app.launch()
      }
     func whenITapOnEnter() {
          app.buttons["enter"].tap()
     }
     func thenIShouldSeeWelcomeMessage() {
          XCTAssert(app.staticTexts["Welcome to XCUITest"].exists)
    }
}

As you can see, in this extension, we have implemented all of our steps using the XCUITest methods that we used in the WelcomeMessage() test. Now if you run the test, all the tests will pass.

Sprinkle Activities for Better Test Reporting

Now that we have applied the BDD concepts to our XCUITests to make them human-readable. We can make them more readable in the Xcode reports by applying the XCTActivity feature of XCTest for each step. With activities sprinkled on each step, we can write steps like this

func givenILaunchedAnApp() {
     XCTContext.runActivity(named: "Given I Launched an App")  { _ in
          XCUIApplication().launch()
        }
}

We can apply this to all the remaining steps. Once we have implemented all the activities our step definition file looks like this:

Sprinkle Activities for Better Test Reporting

Once you run this test and jump to the Xcode test report by right-clicking on the Play button from the test method. You will see the Xcode reports in a much more readable format.

Now our XCUITest and Xcode test reports are much readable since we’ve applied for extensions.

Add Protocols

Swift protocols are similar to the interfaces or abstract classes from other languages. We can use Swift protocols to make contracts between test classes and test methods. Writing protocols is an optional step but it’s good to have it. In our case, we can write a protocol for the Welcome feature in the step definition file which defines the test methods and makes the test class adopt the protocol.

protocol Welcome {
    func testWelcomeMessage()
    func testWelcomeMessageInBDDStyle()
}

Now we can make our test class XCUITest101UITests class to adopt the Welcome protocol. The benefit of creating protocols is you can think of all the test scenarios well in advance and create the contract with your test classes. There is less possibility of forgetting the scenarios to implement.

Try It Yourself

The source code used in this tutorial is available on Github XCUITest101 repository and the protocol-bdd branch here, download the repository and explore the XCUITest with protocol and extension. From the command line, you can get the source code

 $ git clone https://github.com/Shashikant86/XCUITest101
 $ cd XCUITest101
 $ git checkout protoc0l-bdd
 $ open XCUITest101.xcodeproj/

Once the project gets opened in the Xcode 10, then press CMD+U to run the XCUITest. If you want to try it out on some free iOS devices, you can check out what Bitbar has to offer.

Conclusion

In this post, we have applied the Behaviour-Driven Development approach to making our XCUITest tests human readable, scalable and reusable for iOS app testing. We can reuse the step definition anywhere inside the UI test target and create human-readable Xcode reports. However, we have our XCUIElements in the step definitions, which need to be scaled for reusability. In the next post, we will organize UI elements using Swift enumeration for better maintenance of XCUITest tests. Stay Tuned.