What Is Swift Testing?
Swift Testing is Apple’s new, standalone testing framework introduced at WWDC 2024 and shipped with Xcode 16. It’s available without manually adding any dependencies.
Unlike the traditional XCTest, which traces its roots back to ObjC, Swift Testing is built entirely in Swift and leverages language features such as macros, protocol-oriented design, and value types to offer a more idiomatic testing experience.
Tests are marked with a @Test macro instead of relying on specifically-named methods, and assertions use an #expect() macro that captures the full expression for richer failure diagnostics. @Suite macro instead of inheriting tests from XCTestCase. And other handy tools we discover later.
You can run Swift Testing tests side-by-side with existing XCTest cases, allowing for an incremental migration path.
Benefits and real-world examples
More Swifty syntax & semantics
- You don’t have to use the “test” prefix for your functions, just annotate them with @Test
- Expressive assertions: The #expect() macro introspects the boolean expression you write, producing precise diffs on failure. You can use expect for everything instead of the old:
- XCTAssertTrue, XCTAssertFalse
- XCTAssertEqual, XCTAssertNotEqual
- XCTAssertNil, XCTAssertNotNil
- XCTAssertGreaterThan, XCTAssertLessThanOrEqual, …
- Checking optionals you can use #require instead of XCTUnwrap.
- You don’t have to use classes for tests, there is no need to inherit from the XCTestCase class. You can test actors, structs, and just methods easily.
- Lightweight setup/teardown with init, deinit.

Parameters
Every single individual test can define arguments in an array. These arguments will be used as parameters for the test function. The test function runs as many times as many parameters are defined. In the test navigator you can see the test method and its parameters and you can even run the method against the selected parameter individually. This comes very handy when the test method fails against one of the parameters.

Concurrency support
It works out of the box without any boilerplate code. There is built-in concurrency support. You have to write “concurrency: parallel” in the @Test annotation. It has a compile time safety because the signature and the arguments are checked at build time. Async/await syntax is supported.

Suites
Instead of using a class that must inherit from XCTestCase you can use struct to have your tests inside of a scope. In a scope you can run all the tests at once and you can share dependencies across test cases. Since structs will be deallocated when tests are finished and they get out of scope it will improve memory management. The @Suite annotation is optional, as you can see in the example below struct is automatically recognised as a suite.

Tags
It is another way to group tests. After you define your own tags you can use them for multiple tests even across different test suites. You can also use tags for the whole test suite.

Traits
Swift testing offers condition traits to control test execution based on specific conditions. You can enable or disable a test. This is super handy when you have different environments where some tests make sense on one of the environments and make no sense on the others. You can also use this when you want to skip a test on CI. You can also write conditional tests that only run in case there is certain data, a certain operating system, or database.

How we adopted it
We have a multi-modular architecture. Not only the reusable parts of the app are outsourced into swift packages but we also have separate swift packages for every feature. This architecture helps us to adapt to this new framework. Our strategy is when we create a new package we only write Swift tests for it. When we have to modify an already existing package or the main target then we only use Swift testing. So the general rule is that new code is tested by Swift tests. This works with SPM, Xcode, CI out of the box. Since XCTest and Swift testing can work side-by-side no migration is needed. Slowly but surely we are going to replace the XCTests but we are not in a hurry. I suggest doing the same, even though the new framework has a lot of benefits it’s not worth it to spend days, weeks or even months refactoring the already working unit tests. Just:
- Add import Testing in a new test file
- Use @Test and #expect for new cases
- Keep existing XCTestCase-based tests untouched
- Use xcodebuild test or swift test to run both
Final thoughts
Swift Testing is a huge leap forward in how we write and structure unit tests in Swift.
It reduces boilerplate, embraces macros, improves test clarity, supports async/await natively, and gives us powerful features like traits and parameterized tests, tags. It’s ready for production use today — especially in:
- Modular codebases using Swift Package Manager
- New features where testing clarity matters
- CI pipelines that need granular, data-driven testing
Whether you’re improving a small codebase or scaling a team’s architecture — Swift Testing helps you write faster, more expressive, and safer tests.
