swift

A useful extension of XCTestCase

XCTest expectations are great but it is not easy to use them.

Here is a simple extension that makes it possible to wait for a condition until it is fulfilled or timed-out.

extension XCTestCase {

    /// Wait for a condition to be fulfilled
    /// Example:
    ///     var fulfilled = false
    ///     wait(fulfilled)
    public func wait(
        _ condition: @autoclosure @escaping () -> Bool,
        timeout: TimeInterval = 10,
        description: String = "wait condition expectation"
    ) {
        let expectation = self.expectation(description: description)

        func testForCondition() {
            if condition() {
                expectation.fulfill()
            } else {
                DispatchQueue.main.async { testForCondition() }
            }
        }

        testForCondition()

        wait(for: [expectation], timeout: timeout)
    }
}

Let’s talk about the code. The condition is passed as a closure. @autoclosureis a syntactic sugar so that instead of wait({ a==b }) we can write wait(a==b) @escaping tells the compiler that the closure is retained.

Another line we should pay attention to: DispatchQueue.main.async { testForCondition() } The only reason we need DispatchQueue.main.async is that it makes sure that testForCondition is executed once per run-loop.

And finally the usage:

func testWaitForCondition1() throws {
    var fulfilled = false
    let subject = PassthroughSubject<Int, Never>()

    subject
        .debounce(for: .milliseconds(100),
                  scheduler: DispatchQueue.global())
        .sink(receiveCompletion: { _ in
            print("finished")
        }, receiveValue: { value in
            fulfilled = true
        })
        .store(in: &tasks)

    subject.send(1)
    subject.send(2)

    wait(fulfilled)
}

The code can be found here.

A useful extension of XCTestCase

Subscribe to The infinite monkey theorem

Get the latest posts delivered right to your inbox