ST-0021: Targeted Interoperability between Swift Testing and XCTest

Hello Swift Community!

The review of ST-0021 "Targeted Interoperability between Swift Testing and XCTest" starts now and runs through Tuesday, March 10th, 2026. The proposal is available here: swift-evolution/proposals/testing/0021-targeted-interoperability-swift-testing-and-xctest.md at main · swiftlang/swift-evolution · GitHub

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, you need to be on a fairly recent toolchain. On a Mac, probably the simplest way is to use the Xcode 26.4 beta. 26.4 Beta 1 adds what this proposal refers to as "limited" interoperability:

When you call an XCTest or Swift Testing assert within a test from the opposite framework, you will see a runtime issue with warning severity if the assertion failed. (169220281)

On non-Mac platforms, in addition to a recent toolchain (such as main-snapshot), you will also need to reference the try-out-interop branches of both swift-testing and swift-corelibs-xctest from your Package.swift.

...
dependencies: [
  ...
  .package(
    url: "https://github.com/swiftlang/swift-testing.git",
    branch: "try-out-interop"
  ),
  .package(
    url: "https://github.com/swiftlang/swift-corelibs-xctest.git",
    branch: "try-out-interop"
  ),
]
...
targets: [
  ...
  .testTarget(
    ...
    dependencies: [
      ...
      .product(name: "Testing", package: "swift-testing"),
    ]
  )
  ...
]

You will need to also specify an LD_PRELOAD environment variable pointing at the local XCTest build when you run swift test.

LD_PRELOAD=.build/aarch64-unknown-linux-gnu/debug/libXCTest.so swift test

@jerryjrchen has kindly set up this repository as a demonstration, and I have shamelessly copied his instructions.

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!

Rachel Brindle

Review Manager

4 Likes

Taking my Review Manager hat off, and putting on a Reviewer hat:

Overall, I am very excited for this. This is great for both maintainers of third-party assertion helper tools (more on that further down), as well as for enabling migration of tests from XCTest to Swift Testing. As one of the drivers of that migration at my day job, I'm excited to enable the complete interop mode so that we don't have to rewrite all of our test assertion infrastructure as we go on. I appreciate the different interop modes, including the limited mode (which allows for teams to progressively fix their previously falsely-passing tests), even though, as a test maximalist, the idea of allowing people to just merge in code with tests known to fail is wrong.

As the maintainer of the third-party assertion helper tool, Nimble, this interop is great because I'll be able to rewrite my silly logic for sending assertions to either swift testing or xctest (Well, I can rewrite it once I require at least swift 6.3). The logic can change from the current fairly brittle approach to a more direct "report everything to Swift Testing and let the interop handle it". Which makes me happy and will allow me to close a now 5 2-year-old issue as "doesn't matter anymore".

Review manager hat back on.

3 Likes

Exqueeze me: two years.

Overall I'm in favour of the proposal. I have a couple of questions:

  1. Where the proposal mentions "6.X" do we have a sense of what version that will be, or is it intentionally left unspecified?
  2. Can you clarify whether Xcode 26.4 Beta does (or does not?) support this functionality as of today?

Oh wow. Reading comprehension fail on my part. I swear I initially read that issue date as "June 2021". Which does not pass the smell test at all given that Swift Testing itself wasn't revealed until June 2024.

For both of these, I think it would be good for @jerryjrchen to jump in, but to give my opinion:

My understand is that "6.X" is whatever version of Swift this can feasibly be included in once the proposal is accepted. So, if this proposal is accepted in time to be cherry-picked into 6.3, then "6.X" means "6.3". If this proposal isn't accepted until after that cutoff (assuming it gets accepted at all), then 6.X could be some version not yet announced (Disclaimer: I've never worked for Apple, and I have no special knowledge about... any of this).

I did not try this using Xcode 26.4 Beta 1 because I am not willing to install the required macOS version on any of my machines. I can attest that the "Try it out" methodology using Docker works quite well.

It doesn't seem to be, this test passes just fine on the iOS 26.4 sim from Xcode 26.4b2, unless I misunderstand what it's supposed to do.

import XCTest

@Suite
struct CrossoverTests {
    @Test
    func xcTestInteropWorks() {
        XCTFail("failed in XCTFail")
    }
}

@younata Is there a flag or anything I need to enable to see this in Xcode?

I found the environment variable and set it for my test target, still passes.

The Trying It Out section above mentions this item from the Xcode 26.4 Beta release notes (emphasis mine):

  • When you call an XCTest or Swift Testing assert within a test from the opposite framework, you will see a runtime issue with warning severity if the assertion failed. (169220281)

In other words, in that Xcode beta, this interoperability feature only causes warnings, as a way to gracefully introduce the new behavior and avoid breaking existing tests.

I assume you're talking about the environment variable named SWIFT_TESTING_XCTEST_INTEROP_MODE. What value did you specify for that env var?

The spellings of the "mode" names changed late in the Pitch phase, and the current Xcode 26.4 Beta may only honor the original set of names. If you want to try out the elevated mode which the proposal refers to as complete, try using its previous name, permissive, temporarily.

Yes, it needed the permissive setting to take effect, which both showed the runtime issue and failed the test.

It looks like the runtime issue suggests people upgrade to Testing, while the test failure is what I'd expect, which doesn't really match what I'd expect from the release note.
Screenshot 2026-02-24 at 8.44.47 PM

Thanks for trying this out! To clarify, Xcode 26.4 Beta features interoperability "out of the box." You shouldn't need to set anything to enable default "limited" (formerly "advisory") interoperability. This is also the mode described in the release notes.

Assuming the proposal is accepted, I'm aiming for 6.4 rather than trying to cherry-pick it into 6.3.

1 Like

Big +1 to the proposal overall, the no-op behavior is a bit of a speed bump for piecemeal migrations. Particularly big fan of the interoperability modes.

2 Likes