Pavel Gatilov Pavel Gatilov - 3 months ago 43
Swift Question

Stub data for XC UI Tests

So, let me explain my problem first.

I don't want to relay on my web-server data, I want to stub data for my XCUITests.
So, I will be sure that it returns correct data in 100% times, as well as sometimes I need to test some specific(e.g. errors, or empty state) cases, which web-server may not return in exact that moment.
So, what I have tried, it is to run the local server in my XCUITest and then stub some requests, but turns out it didn't work out because XC UI Tests are running in complete separate bundle(even separate process) and local server can't be binded to localhost, so my Main app bundle can't connect to this server.

Another solution that I've tried is to pass some params through XCUIApplication().launchArguments, and based on this params - run stubs on main app, but then - it is a bit of a problem: "I have local-proxy server in main app", which I need only for UI testing.

I know, that also, I can just create stub-server and expose it to the web, so to say, create kind of development-env just for UI tesitng, but it seems to extreme for me. Because in that case maintaining only UI tests for my project is a big effort.

So, my question is, does anyone have better solution? Is there any way to get around this problem without modifying your main app and running external web-server?

Answer

You can use SBTUITestTunnel. This library allows to dynamically stub network requests (among other things) in a simple fashion.

The easiest way to add the library is to use cocoapods, then override the initialize method of your AppDelegate:

import UIKit
import SBTUITestTunnel

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    override class func initialize() {
        SBTUITestTunnelServer.takeOff()
        super.initialize()
    }

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        return true
    }
}

Once you added that, you're ready to go. You can add/remove stub to network requests to your UI Tests like in the example below:

func testThatSomethingStubbedWorks() {
  let app = SBTUITunneledApplication()
  app.launch()

  let stubId = app.stubRequestsMatching:SBTRequestMatch(SBTRequestMatch.URL("google.com"), returnJsonDictionary: ["key": "value"], returnCode: 200, responseTime: SBTUITunnelStubsDownloadSpeed3G)

  // from here on network request containing 'google.com' will return a JSON {"request" : "stubbed" }
  ...

  app.stubRequestsRemoveWithId(stubId) // To remove the stub either use the identifier
  app.stubRequestsRemoveAll() // or remove all active stubs
}