Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Stubbing XCUITests With Vapor Server Side Swift Framework

DZone's Guide to

Stubbing XCUITests With Vapor Server Side Swift Framework

In this tutorial, you'll learn how to use the Vapor server-side Swift framework to stub network requests in XCUITests.

· Mobile Zone ·
Free Resource

There is no doubt that Swift is an awesome language for developing native apps for Apple platforms like iOS, macOS, watchOS, and tvOS. However, being a new language, Swift lacks a lot of testing features like we see in other programming languages, like Ruby. Mocking classes with protocols in Swift is terribly hard and there are limited options to stub the network requests. Mocking is fragile and hard, and it gets harder when it comes to Swift. There are no mature mocking libraries available in Swift to generate mocks like there are in Java, Ruby, Python, and other languages. The developer has to write all the mocks by hand and tightly couple the test code with the production code. There is a great article on network testing in iOS here to understand more about how to mock with protocols.

Stubbing is another way to test our code without having to rely on a backend or network.  Using stubs, we can still achieve the goals of network testing and we don’t have to tightly couple test code to production code. We will see details of the libraries that can be used for stubbing. Some of the most popular libraries are OHHTTPStubsMockingjay, and Hippolyte for XCTest (unit) testing. The user interface tests go through the network layer and cover testing all the aspects of network testing. Apple has the XCUITest framework to cover Xcode UI testing. There are some libraries which we can use to stub the network for UITest. Some of the popular libraries are SwifterSBTUITestTunel, and Embassy for XCUITest (UITest). However, there are new server-side Swift frameworks emerging in the market, like Vapor, Kitura, and Perfect, which also can be used for the stubbing network requests. In this post, we will see how we can use the Vapor server-side Swift framework to stub the network requests.

Stubbing Network Requests for XCUITests

Apple announced UI Testing support in Xcode at WWDC 2015. The UI tests are designed to be completely black box without having any access to main application code and data. XCUITest uses two standalone processes: Test Runner and Target app. Test runner launches the target app and uses the accessibility capabilities built into UIKit to interact with the app running in a separate process. While mocking or stubbing UITest, we have a few options:

  • Mock and Pass Launch Arguments/Environments

This means you cannot mock the API directly. The only way to communicate with the proxy app is to pass the Launch Arguments or Launch Environments. We can pass mock the API and create a launch environment variable and pass it to XCUITests, but it requires a lot of hard work. The code is surrounded by if-else statements to determine the build configuration, which doesn’t sound great.

  • Stub Backend with the web server and return responses

The other option is to stub the network calls and get the desired data back from the server. In order to do this, we need to mock the backend and point our app to use those endpoints. The question is, can we still use the stub services mentioned above for unit tests? We can still use those libraries to stub out the network requests, but sometimes it requires a change in the logic of the main app and needs to refactor things, which adds extra work. We need to add a lot of test code in the production code.

In my previous post on network stubbing options for XCUITest, we covered options like SwifterSBTUITestTunel, and Embassy for XCUITest. Now let’s cover stubbing with Vapor in detail.

Stub Using Vapor: Example App

The Server Side Swift frameworks are not production-ready yet, but we can use them for the purpose of stubbing the XCUITest. I haven’t read about anybody doing that on the internet, but it works perfectly. There are a few server-side Swift frameworks available on the market,like  PerfectKituraZewo, and Vapor, but for this demo, we will use Vapor. The reason for choosing Vapor is that it’s fairly easy to learn and its community is growing fast. It's purely written in Swift and comes with the Vapor toolbox, which is easy to get started.

In order to demonstrate this feature, I have created a demo app that displays GitHub user information when we enter a GitHub username in the text field. You can clone that app from GitHub at Vapor-XCTest, which has XCUITests with and without the stub.

You need to have Xcode 9+ and OpenSSL installed, then we can check out the demo app.

$ git clone https://github.com/Shashikant86/Vapor-XCTest
$ cd Vapor-XCTest
$ open Vapor-XCTest.xcodeproj

This example app shows the GitHub user information. It asks users to enter your GitHub username, and once you've clicked on the "submit" button, it displays all the user details. Now you will see the example app with a couple of demo UITests, one with real network request and another with the stubbed network request with Vapor.

Try running LocationCheckWithoutStub.swift, which makes a real network request. You can see that it uses the launchEnvironment value from the real server,  app.launchEnvironment = ["BASEURL" : "https://api.github.com"] , to make a real network request.

Now, navigate to the Vapor-Server directory where we have all Vapor setup. Build the project and run the server. The server will send the dummy response that stubbed in the main.swift  file.

$ cd Vapor-Server
$ swift build
$ .build/debug/Vapor-Server serve

This will start vapor server on port 8080 and listen to the response from the local Vapor server.

Now run the UITest LocationCheckWithVaporStub.swift, which uses the local response. You can see the launchEnvironment value from the local network app.launchEnvironment = ["BASEURL" : "https://localhost:8080"] . You can see that the test will pass with the response stubbed data.

Stub Using Vapor: Your App

Now that we have seen that example app stubbed with the stubbed data using Vapor, let’s explore the process of how we can set up your app to use stubbed responses of the Vapor server.

  • Allow Local Networking 

In order to use stubbed data from Vapor, we need to do the little bit of setup to configure our main app to listen to the local network calls. This can be done by the editing the main app Plist file by adding the key:

<key>NSAppTransportSecurity</key>
    <dict>
        <!--To support localhost -->
        <key>NSAllowsLocalNetworking</key>
        <true/>
        <!--To continue to work for iOS 9 -->
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>


This will allow networking for iOS apps. However, you should do this only for the debug build configuration or similar, not for the release configuration.

  • Set up Launch Arguments/Environments to use local responses

In order to replace the real network requests with local requests, we need to set up some Launch Arguments/Environments that can be used by the XCUITests. By doing that, we can do the tests for both real requests and stubbed requests by passing launch arguments or environments.

  • Get Vapor inside the iOS app

There are the couple of ways to get Vapor in the iOS project. We can either use Vapor toolbox or we can get Vapor using Swift Package Manager, as mentioned here. I would recommend getting it using Swift Package Manager, as we probably don’t need much of the web framework that Vapor toolbox provides. Refer to an example of Vapor Server from the demo app and the package.swift  file here.

  • Start Stubbing network requests 

Once you have all the necessary tooling, it’s time to actually stub network requests. Vapor has comprehensive documentation on how to work with requests and responses in JSON.  Like in the demo app, we have stubbed the request to /users/shashikant86 to return a stubbed response.

import Vapor
 
let drop = try Droplet()
 
drop.get("users/shashikant86") { request in
    var json = JSON()
    try json.set("location", "StubLocation")
    try json.set("name", "StubName")
    try json.set("blog", "http://shashikantjagtap.net")
    try json.set("followers", 100000000000000000000000000000)
    try json.set("public_repos", 100000000000000000000000000)
    return json
}
 
try drop.run()

You can see that with stubbing, we can actually test the edge cases and scenarios that are hard to test with real data. A good example would be testing a demo GitHub app for a user who has a trillion followers. In real life, nobody has that many followers, but we can stub it and test it as shown in the code above.

I have given a presentation on Vapor London, Meetup at the BBC office. The slides are at Vapor London March 2018 from Shashikant Jagtap.

Conclusion

Using Server Side Swift Frameworks like Vapor and others, we can stub the network requests that can be used by XCUITests for running user interface tests. We can use the benefits of the Swift language for stubbing network calls, which eliminates the need to run the servers using other programming languages like Ruby or Java. What do you think of this approach? Or do you still prefer nodeJSSinatra servers, or would you use lightweight servers like Swifter for stubbing requests for XCUITest? Contact me on Twitter @Shashikant86 or comment below.

Topics:
mobile ,xcuitest ,ios ,mobile testing ,swift ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}