ST-0016: Test cancellation

Hello Swift Community!

The review of ST-0016 “Test cancellation” begins now and runs through Wednesday November 5, 2025. The proposal is available here:

https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0016-test-cancellation.md

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager. When messaging the review manager directly, please keep the proposal link at the top of the message.

Trying it out

To try this feature out, add a dependency to the main branch of swift-testing to your project:

...
dependencies: [
  ...
  .package(url: "https://github.com/swiftlang/swift-testing.git", branch: "main"),
]

Then, add a target dependency to your test target:

.testTarget(
  ...
  dependencies: [
    ...
    .product(name: "Testing", package: "swift-testing"),
  ]
)

Finally, import Swift Testing using @_spi(Experimental) import Testing.

What goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

More information about the Swift evolution process is available at

swift-evolution/process.md at main · swiftlang/swift-evolution · GitHub

Thank you for contributing to Swift!

Maarten Engels

Review Manager

3 Likes

Just to be clear: Task.isCancelled will return true if I read this value in my testing library after a Test has been cancelled?

Hi Lennart,

I'll also let @grynspan weigh in.

Quick test:

Is this where you would like to inspect this value? Or are you looking at some other place/have another example in mind?

Minimal case, with Isolation logic removed:

func countIssues(perform closure: () async throws -> Void) async -> Int {
    let handler = IssueHandlingTrait.filterIssues { _ in
        // omitted
    }
    try? await handler.provideScope(for: Test.current!, testCase: Test.Case.current) {
        closure()
    }
    return 0 // omitted
}

public func propertyCheck(
    perform body: () async throws -> Void
) async {
    let foundIssueCount = await countIssues {
        try await body()
    }

    // Will this work?
    guard !Task.isCancelled else { return }
}

await propertyCheck {
    try Test.cancel()
}

I had to wrangle your example a bit to satisfy the compiler. But it seems to work.

This is the code I ran:

func countIssues(perform closure: @Sendable () async throws ->  Void) async -> Int {
    let handler = IssueHandlingTrait.filterIssues { _ in
        // omitted
        true
    }
    try? await handler.provideScope(for: Test.current!, testCase: Test.Case.current) {
        try? await closure()
    }
    return 0 // omitted
}

public func propertyCheck(
    perform body: @Sendable () async throws -> Void
) async {
    let foundIssueCount = await countIssues {
        try await body()
    }

    // Will this work?
    print(Task.isCancelled ? "Task is cancelled" : "Task not cancelled") // >> Task is cancelled
    guard !Task.isCancelled else { return }
}

@Test func asyncPropertyCheck() async {
    await propertyCheck {
        try Test.cancel()
    }
}

And found Task is cancelled in the output.

1 Like

Yes. From the proposal text:

Cancelling a test or test case implicitly cancels its associated task (and any child tasks thereof) as if Task.cancel() were called on that task.

isCancelled will thus be true when queried from within the current test's (or test case's) task or any of its child tasks.

Perhaps simpler:

@Test func example() {
  _ = try? Test.cancel()
  #expect(Task.isCancelled)
}
2 Likes

That is an interesting snippet. Maybe it's a good idea to add this to the proposal as an example on how to check if a test (case) cancelled?

2 Likes

Sure, I can do that.

2 Likes

I’m in favor of this new API as there are unfortunately some tests we have written that skip certain cases, but today it’s not marked as skipped and instead is just marked as passing.

This feels like a good API, as the proposal draws a lot of parallels to the Task APIs, which underpins a lot of the Testing library.

2 Likes

The review has been concluded and the proposal has been accepted.