Created
December 4, 2025 09:47
-
-
Save andr3a88/6123ced8897a74f4c2b6bd69c65b2a6a to your computer and use it in GitHub Desktop.
Asserts that an asynchronous expression throws an error.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import XCTest | |
| /// Asserts that an asynchronous expression throws an error. | |
| /// (Intended to function as a drop-in asynchronous version of `XCTAssertThrowsError`.) | |
| /// | |
| /// Example usage: | |
| /// | |
| /// await assertThrowsAsyncError( | |
| /// try await sut.function() | |
| /// ) { error in | |
| /// XCTAssertEqual(error as? MyError, MyError.specificError) | |
| /// } | |
| /// | |
| /// - Parameters: | |
| /// - expression: An asynchronous expression that can throw an error. | |
| /// - message: An optional description of a failure. | |
| /// - file: The file where the failure occurs. | |
| /// The default is the filepath of the test case where you call this function. | |
| /// - line: The line number where the failure occurs. | |
| /// The default is the line number where you call this function. | |
| /// - errorHandler: An optional handler for errors that `expression` throws. | |
| func assertThrowsAsyncError<T>( | |
| _ expression: @autoclosure () async throws -> T, | |
| _ message: @autoclosure () -> String = "", | |
| file: StaticString = #filePath, | |
| line: UInt = #line, | |
| _ errorHandler: (_ error: Error) -> Void = { _ in } | |
| ) async { | |
| do { | |
| _ = try await expression() | |
| // expected error to be thrown, but it was not | |
| let customMessage = message() | |
| if customMessage.isEmpty { | |
| XCTFail("Asynchronous call did not throw an error.", file: file, line: line) | |
| } else { | |
| XCTFail(customMessage, file: file, line: line) | |
| } | |
| } catch { | |
| errorHandler(error) | |
| } | |
| } | |
| extension XCTestCase { | |
| @MainActor | |
| func execute( | |
| withTimeout timeout: TimeInterval, | |
| file: StaticString = #filePath, | |
| line: UInt = #line, | |
| workItem: @escaping () async throws -> Void | |
| ) { | |
| let expectation = expectation(description: "wait for async function") | |
| var workItemError: Error? | |
| let captureError = { workItemError = $0 } | |
| let task = Task { | |
| do { | |
| try await workItem() | |
| } catch { | |
| captureError(error) | |
| } | |
| expectation.fulfill() | |
| } | |
| waitForExpectations(timeout: timeout) { _ in | |
| if let error = workItemError { | |
| XCTFail("\(error)", file: file, line: line) | |
| } | |
| task.cancel() | |
| } | |
| } | |
| } | |
| extension AsyncSequence { | |
| /// Accumulates the results of an `AsyncSequence` into an array. | |
| func accumulate() async rethrows -> [Element] { | |
| var results: [Element] = [] | |
| for try await item in self { | |
| results.append(item) | |
| } | |
| return results | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment