Swift Testing Migrator is a command-line tool to automate the migration of XCTest-based test suites to the Swift Testing framework. Built with SwiftSyntax and Swift Argument Parser.
- Complete class transformation: Converts
XCTestCaseclasses to Swift Testing structs or classes - Smart method rewriting: Adds
@Testattributes to test methods - Setup method conversion: Turns
setUp/setUpWithErrorinto initializers - Assertion mapping: Maps XCTest assertions to Swift Testing equivalents
- Code cleanup: Removes unnecessary inheritance and modifiers
- Format preservation: Keeps your comments, whitespace, and code structure intact
- Batch processing: Handles individual files or entire directories
- Swift: 6.1 or later
- Platform: macOS 15 or later, Linux, and Windows
Clone and build the migrator:
git clone https://github.com/your-username/swift-testing-migrator.git
cd swift-testing-migrator
swift build --configuration releaseSingle file migration:
swift-testing-migrator MyTestFile.swiftDirectory migration (recommended):
swift-testing-migrator Tests/ --in-place --recursiveHigh-performance batch migration:
swift-testing-migrator Tests/ --in-place --recursive --parallelPreview changes before applying:
swift-testing-migrator Tests/ --recursive # Outputs to stdoutMigrate to class-based test suites:
swift-testing-migrator Tests/ --in-place --recursive --use-class| Option | Description |
|---|---|
--in-place |
Modify files directly instead of printing to stdout |
--recursive |
Process all Swift files in subdirectories |
--parallel |
Enable parallel processing (recommended with --in-place) |
--use-class |
Convert XCTestCase to class-based test suites instead of structs |
--help |
Show usage information |
| XCTest | Swift Testing |
|---|---|
XCTAssert(condition) |
#expect(condition) |
XCTAssertTrue(condition) |
#expect(condition) |
XCTAssertFalse(condition) |
#expect(!condition) |
XCTAssertNil(value) |
#expect(value == nil) |
XCTAssertNotNil(value) |
#expect(value != nil) |
XCTAssertEqual(a, b) |
#expect(a == b) |
XCTAssertNotEqual(a, b) |
#expect(a != b) |
XCTAssertIdentical(a, b) |
#expect(a === b) |
XCTAssertNotIdentical(a, b) |
#expect(a !== b) |
XCTAssertGreaterThan(a, b) |
#expect(a > b) |
XCTAssertGreaterThanOrEqual(a, b) |
#expect(a >= b) |
XCTAssertLessThan(a, b) |
#expect(a < b) |
XCTAssertLessThanOrEqual(a, b) |
#expect(a <= b) |
try XCTUnwrap(optional) |
try #require(optional) |
XCTFail("message") |
Issue.record("message") |
Note: File and line parameters (e.g.,
file: #file, line: #line) are omitted in the migrated code, as they are not required in Swift Testing.
| XCTest | Swift Testing |
|---|---|
XCTAssertThrowsError(try expression()) |
#expect(throws: (any Error).self) { try expression() } |
XCTAssertThrowsError(try expression()) { error in /* handle */ } |
let error = #expect(throws: (any Error).self) { try expression() } |
XCTAssertNoThrow(try expression()) |
#expect(throws: Never.self) { try expression() } |
| XCTest | Swift Testing | Notes |
|---|---|---|
import XCTest |
import Testing |
|
class MyTests: XCTestCase |
struct MyTests or class MyTests |
Classes are converted to structs by default; use --use-class to keep as classes. |
func testSomething() { } |
@Test func testSomething() { } |
Methods starting with test are annotated with @Test. |
override func setUp() async throws |
init() async throws |
setUp/setUpWithError are converted to initializers. |
override func tearDown() |
deinit |
Async or throwing tearDown methods are not supported and require manual migration. |
swift-testing-migrator/
├── Sources/
│ ├── TestingMigrator/ # Core migration engine
│ └── swift-testing-migrator/ # Command-line interface
├── Tests/
│ └── TestingMigratorTests/ # Comprehensive test suite
└── Package.swift # Swift Package Manager configuration
- swift-syntax: For parsing and transforming Swift source code
- swift-argument-parser: For command-line interface
# Debug build (faster compilation)
swift build
# Release build (optimized performance)
swift build --configuration release# Run all tests
swift testTo benchmark the migrator's performance on your system:
# Run performance test with 1,000 files containing 10 methods each
./measure_performance.sh 1000 --methods 10This generates test fixtures, runs benchmarks for all processing modes, and provides detailed timing comparisons to help you pick the best configuration for your needs.
Tested on 1,000 Swift test files with 10 methods each
| Mode | Processing | Time |
|---|---|---|
| Read-only | Sequential | 2.02s |
| Read-only | Parallel | 2.63s |
| In-place | Sequential | 548ms |
| In-place | Parallel | 209ms |
Note: Tested on a Mac mini M4 Pro with 24GB RAM. Results may vary based on system configuration.
We love contributions! Here’s how you can help:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-improvement - Make your changes with tests
- Run the test suite:
swift test - Submit a pull request
- Add tests for new features or bug fixes
- Keep code formatting consistent
- Update documentation for user-facing changes
- Include example transformations in your PR description
- Some XCTest-specific features (like performance testing) don’t have direct Swift Testing equivalents
- The tool doesn’t currently support migrating tests that use
XCTestExpectation, asynchronous tests,XCTSkip, orXCTSkipIf; these need manual migration - Migration of
XCTestCasesubclasses with complex inheritance hierarchies may require manual adjustments - It’s a good idea to run formatting tools like
swift-formatafter migration to ensure code style consistency
This project is licensed under the MIT License - see the LICENSE file for details.
Ready to modernize your test suite? Get started with Swift Testing Migrator today and experience the power of Swift Testing! 🚀