Testing Closure-based asynchronous APIs

Like Sergey, I had a needed to test code which provides a synchronous fire-and-forget interface to an asynchronous process. This kind of API model makes sense for some components.

The solution I came up with was more... brute force: sleep the Task in the confirmation() closure. Sleeps in unit tests aren't great, but it seems to me that the risk of test flakiness is directly equivalent to using XCTest expectations with a timeout, so I'm willing to use them in what are fairly rare cases.

@Test func testSomething() async throws {
        var thing = Something()
        try await confirmation(expectedCount: 1) { called in
            thing.customPost { (req: URLRequest, _: Data) in
                // expectations for the the resultant action
                called()
            }
            thing.triggerFireAndForgetAction()
            try await Task.sleep(for: .seconds(0.01))
        }
    }
1 Like