11# Targeted Interoperability between Swift Testing and XCTest
22
3- - Proposal: [ ST-0021] ( 0021-xctest -interoperability.md )
3+ - Proposal: [ ST-0021] ( 0021-targeted -interoperability-swift-testing-and-xctest .md )
44- Authors: [ Jerry Chen] ( https://github.com/jerryjrchen )
55- Review Manager: [ Rachel Brindle] ( https://github.com/younata )
66- Status: ** Accepted**
77- Implementation: [ swiftlang/swift-testing #1523 ] ( https://github.com/swiftlang/swift-testing/pull/1523 ) ,
8- [ swiftlang/swift-testing #1573 ] ( https://github.com/swiftlang/swift-testing/pull/1573 ) ,
9- [ swiftlang/swift-testing #1542 ] ( https://github.com/swiftlang/swift-testing/pull/1542 ) ,
10- [ swiftlang/swift-testing #1516 ] ( https://github.com/swiftlang/swift-testing/pull/1516 ) ,
11- [ swiftlang/swift-testing #1512 ] ( https://github.com/swiftlang/swift-testing/pull/1512 ) ,
12- [ swiftlang/swift-testing #1478 ] ( https://github.com/swiftlang/swift-testing/pull/1478 ) ,
13- [ swiftlang/swift-testing #1439 ] ( https://github.com/swiftlang/swift-testing/pull/1439 ) ,
14- [ swiftlang/swift-testing #1369 ] ( https://github.com/swiftlang/swift-testing/pull/1369 ) ,
15- [ swiftlang/swift-corelibs-xctest #525 ] ( https://github.com/swiftlang/swift-corelibs-xctest/pull/525 )
8+ [ swiftlang/swift-testing #1573 ] ( https://github.com/swiftlang/swift-testing/pull/1573 ) ,
9+ [ swiftlang/swift-testing #1542 ] ( https://github.com/swiftlang/swift-testing/pull/1542 ) ,
10+ [ swiftlang/swift-testing #1516 ] ( https://github.com/swiftlang/swift-testing/pull/1516 ) ,
11+ [ swiftlang/swift-testing #1512 ] ( https://github.com/swiftlang/swift-testing/pull/1512 ) ,
12+ [ swiftlang/swift-testing #1478 ] ( https://github.com/swiftlang/swift-testing/pull/1478 ) ,
13+ [ swiftlang/swift-testing #1439 ] ( https://github.com/swiftlang/swift-testing/pull/1439 ) ,
14+ [ swiftlang/swift-testing #1369 ] ( https://github.com/swiftlang/swift-testing/pull/1369 ) ,
15+ [ swiftlang/swift-corelibs-xctest #525 ] ( https://github.com/swiftlang/swift-corelibs-xctest/pull/525 )
1616- Review: ([ pitch] ( https://forums.swift.org/t/pitch-targeted-interoperability-between-swift-testing-and-xctest/82505 ) ,
17- [ review] ( https://forums.swift.org/t/st-0021-targeted-interoperability-between-swift-testing-and-xctest/84965 ) ,
18- [ acceptance] ( https://forums.swift.org/t/accepted-st-0021-targeted-interoperability-between-swift-testing-and-xctest/85331 ) )
17+ [ review] ( https://forums.swift.org/t/st-0021-targeted-interoperability-between-swift-testing-and-xctest/84965 ) ,
18+ [ acceptance] ( https://forums.swift.org/t/accepted-st-0021-targeted-interoperability-between-swift-testing-and-xctest/85331 )
19+ [ amendment] ( https://forums.swift.org/t/amendment-st-0021-targeted-interoperability-between-swift-testing-and-xctest/86142 ) ,
20+ [ amended] ( https://forums.swift.org/t/amended-st-0021-targeted-interoperability-between-swift-testing-and-xctest/86479 ) )
21+
22+ > Apr 2026: Amended to change the definition of limited interop mode.
1923
2024## Introduction
2125
@@ -183,30 +187,60 @@ Here are some concrete examples:
183187
184188- ** None** : No interop, which is the status quo prior to this proposal.
185189
190+ For the remaining modes, ** Swift Testing API will behave as expected when used
191+ in XCTest** . This includes reporting any assertion failures as errors within an
192+ XCTest test case. As a result, any interop mode will enable you to incrementally
193+ migrate your assertions to Swift Testing if desired.
194+
195+ ** XCTest API used in Swift Testing tests** behaves differently based on interop
196+ mode:
197+
186198- ** Limited** : Test failures that were previously ignored are reported as
187199 runtime warning issues. It also includes runtime warning issues for XCTest API
188- usage in a Swift Testing context. This is for projects which do not want to
189- see new test failures surfaced due to interoperability.
200+ usage in a Swift Testing context.
190201
191202- ** Complete** : This is the [ default interoperability
192- mode] ( #source-compatibility ) , which surfaces test failures that were
193- previously ignored. It also includes runtime warning issues
194- for XCTest API usage in a Swift Testing context.
203+ mode] ( #source-compatibility ) , which surfaces all test failures that were
204+ previously ignored. It also includes runtime warning issues for XCTest API
205+ usage in a Swift Testing context.
195206
196207- ** Strict** : Warning issues included in the complete mode can be easily
197208 overlooked, especially in CI. The strict mode guarantees that no XCTest API
198209 usage occurs when running Swift Testing tests by turning those warnings into a
199210 ` fatalError ` .
200211
212+ Here is a concrete example of how interop assertion failures behave under the
213+ different modes:
214+
215+ ``` swift
216+ class FooTests : XCTestCase {
217+ func testInterop () {
218+ // None: No-op
219+ // Limited: ❌ "Interop failure"
220+ // Complete: ❌ "Interop failure"
221+ // Strict: ❌ "Interop failure"
222+ Issue.record (" Interop failure" )
223+ }
224+ }
225+
226+ @Test func `Test Interop`() {
227+ // None: No-op
228+ // Limited: ⚠️ "Interop failure", ⚠️ Adopt Swift Testing primitives
229+ // Complete: ❌ "Interop failure", ⚠️ Adopt Swift Testing primitives
230+ // Strict: 💥 fatalError: Adopt Swift Testing primitives
231+ XCTFail (" Interop failure" )
232+ }
233+ ```
234+
201235Configure the interoperability mode when running tests using the
202236` SWIFT_TESTING_XCTEST_INTEROP_MODE ` environment variable:
203237
204- | Interop Mode | Issue behavior across framework boundary | ` SWIFT_TESTING_XCTEST_INTEROP_MODE ` |
205- | ------------ | -------------------------------------------------------------------------- | -------------------------------------------- |
206- | None | No-op | ` none ` |
207- | Limited | XCTest API: ⚠️ Runtime Warning Issue. All Issues: ⚠️ Runtime Warning Issue | ` limited ` |
208- | Complete | XCTest API: ⚠️ Runtime Warning Issue. All Issues: ❌ Test Failure | ` complete ` , or empty value, or invalid value |
209- | Strict | XCTest API: 💥 ` fatalError ` . Swift Testing API: ❌ Test Failure | ` strict ` |
238+ | Interop Mode | ` SWIFT_TESTING_XCTEST_INTEROP_MODE ` |
239+ | ------------ | -------------------------------------------- |
240+ | None | ` none ` |
241+ | Limited | ` limited ` |
242+ | Complete | ` complete ` , or empty value, or invalid value |
243+ | Strict | ` strict ` |
210244
211245## Source compatibility
212246
@@ -225,9 +259,12 @@ lead to situations where previously "passing" test code now starts showing
225259failures. We believe this should be a net positive if it can highlight actual
226260bugs you would have missed previously.
227261
228- You can use ` SWIFT_TESTING_XCTEST_INTEROP_MODE=limited ` in the short-term
229- to revert any changes to test pass/fail outcomes as a result of
230- interoperability.
262+ You can revert any changes in the short-term to test pass/fail outcomes as a
263+ result of interoperability:
264+
265+ - ` SWIFT_TESTING_XCTEST_INTEROP_MODE=limited ` reduces issue severity from error
266+ to warning for XCTest issues used in Swift Testing tests.
267+ - ` SWIFT_TESTING_XCTEST_INTEROP_MODE=none ` completely turns off interop.
231268
232269## Integration with supporting tools
233270
@@ -300,6 +337,50 @@ mode would record a runtime warning issue and continue the remaining tests,
300337which we believe strikes a better balance between notifying users yet not being
301338totally disruptive to the testing flow.
302339
340+ ### Warning severity for Swift Testing in Limited interop mode
341+
342+ In the original version of this proposal, limited interop mode converted test
343+ failures that were previously ignored into runtime warning issues for ** both**
344+ XCTest and Swift Testing API. The goal was to minimize disruptions to existing
345+ projects that may inadvertently be calling Swift Testing API within XCTest
346+ tests.
347+
348+ This had the unfortunate side effect that if you did as suggested in the
349+ proposal and switched from ` XCTFail() ` -> ` Issue.record() ` , interop consequently
350+ degraded your assertion errors to warnings, which would effectively reduce your
351+ test coverage if you weren't careful!
352+
353+ ``` swift
354+ func someHelperOld () {
355+ XCTFail (" Native failure" )
356+ }
357+
358+ func someHelperNew () {
359+ Issue.record (" Interop failure" )
360+ }
361+
362+ class FooTests : XCTestCase {
363+ func testInterop () {
364+ // Limited interop mode: switch from old -> new, demotes to warning
365+ someHelperOld () // ❌ "Native failure"
366+ someHelperNew () // ⚠️ "Interop failure"
367+ }
368+ }
369+ ```
370+
371+ The current proposal does not have this issue. However, limited interop
372+ mode can cause new test failures if an existing project inadvertently calls
373+ Swift Testing API within an XCTest test. We think this trade-off is worth it:
374+
375+ - This likely surfaces actual bugs in such projects, so error severity is
376+ warranted.
377+
378+ - Since Swift Testing is newer than XCTest, existing projects are more likely to
379+ have test code and helpers that use XCTest API. Calling XCTest API in Swift
380+ Testing tests is therefore more common than the other direction.
381+
382+ - Users can use the none interop mode to opt-out of interop.
383+
303384### Alternative methods to control interop mode
304385
305386- ** Build setting:** e.g. a new ` SwiftSetting ` that can be included in
0 commit comments