Skip to content

q231950/the-stubborn-network

Repository files navigation

The Stubborn Network Logo

An opiniated stubbing machine.

The Stubborn Network makes your SwiftUI development more efficient and UI tests more reliable by stubbing responses of your network requests. It makes it easy to record new stubs and it speeds things up!

👩🏻‍🎨 Stubbed UI Tests

UI tests benefit from The Stubborn Network's ability to easily record actual network requests and then play them back. This makes your tests more stable and the backend won't interfere with you testing the flow of your application.

In order to record and playback stubs you need to

  • configure your URLSession to use The Stubborn Network's URLProtocol in your network layer when running tests
  • the tests on the other hand are required to inform The Stubborn Network which stub sources, in other words - which stubs to use for which test case

App Configuration

The custom URLProtocol will make your standard URLSession of your network client a stubbed variant during UI test execution. This happens inside your application:

Example how to set the custom URL protocol

let configuration = URLSessionConfiguration.default

if ProcessInfo().isUITesting {
    StubbornNetwork.standard.insertStubbedSessionURLProtocol(into: configuration)
    StubbornNetwork.standard.bodyDataProcessor = SensitiveDataProcessor()
    StubbornNetwork.standard.requestMatcherOptions = RequestMatcherOptions([.requestBody, .url, .httpMethod])
}

let urlSession = URLSession(configuration: configuration)

Test Configuration

There are 3 parameters passed in as environment variables to the application under test in order to specify that we want to stub network responses, what to stub, where to find/place them. Some of these will be handled more elegantly in the future:

  1. each test assigns its function name to STUB_NAME to create a dedicated stub source with stubs for network requests of each individual test case
  2. the "STUB_PATH" points to the directory where the stub source with the stubs is stored
  3. the THE_STUBBORN_NETWORK_UI_TESTING parameter simply indicates to the application (see App Configuration above) that we are in a test environment. This is useful to separate between testing and debug/release modes on the other hand
Example UI Test

override func setUp() {
    super.setUp()

    app = XCUIApplication()
    setupSnapshot(app)

    let processInfo = ProcessInfo()
    app.launchEnvironment["STUB_NAME"] = self.name
    app.launchEnvironment["STUB_PATH"] = "\(processInfo.environment["PROJECT_DIR"] ?? "")/BTLBUITests/Stubs"
    app.launchEnvironment["THE_STUBBORN_NETWORK_UI_TESTING"] = "YES"

    app.launch()
}

func testBytesText() {
    /// In the test itself nothing needs to be changed
    app.buttons["Download"].tap()

    let bytesText = app.staticTexts["417 bytes"]
    wait(forElement:bytesText, timeout:1)
}

Re-record Stubs

If a stub needs to be re-recorded it's as easy as just deleting it!

You can find your stubs at the path defined in the test cases and they typically look similar to this this:

XZYUITests_testABC.json

XZYUITests is the name of your test case (XCTestCase) and testABC the name of the test (the test function).

Contribute

Run Tests

You can run tests either from within Xcode (cmd+U) or from the command line: export STUB_DIR='./stubs' && swift test.

Stubs are stored and read during test execution. They can be found in a temporary directory, for example: "file:///private/tmp/StubbornNetworkTests/67BED946-88E0-4F8C-98C7-7A23812D647A-3620-000001390930169C"