MCPInstall

test-modernizer

Modernize test suites to use modern Swift Testing features or migrate from XCTest.

Installs
698
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/superagents-lab/xcode27-skills --skill test-modernizer
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads test-modernizer automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Test Modernizer

Apply when: user asks to modernize, update, migrate, supercharge, or convert their tests. XCTest should be migrated to Swift Testing when possible, existing Swift Testing tests should be evaluated to see if they could be better structured adopting newer features.

Do not apply when: user asks to write new tests from scratch (without existing XCTest code), user asks about XCTest features only, user only asks about test results or test running, user is asking to update tests to cover new functionality rather than updating the tests themselves, user is debugging test failures without mentioning migration, user has UI automation tests using XCUI* APIs (these cannot be migrated to Swift Testing).

Migration Reference

Imports

Replace import XCTest with import Testing. A file can import both if it contains mixed test content during incremental migration.

When removing import XCTest, check whether the file uses Foundation types (URL, CharacterSet, ProcessInfo, Data, etc.). XCTest re-exports Foundation, so add import Foundation if needed.

Test Classes to Suites

Remove XCTestCase inheritance. Prefer struct over class:

  • final class FoodTruckTests: XCTestCase { ... } -> struct FoodTruckTests { ... }

setUp/tearDown to init/deinit

Replace override func setUp() with init() (can be async throws). Replace override func tearDown() with deinit. If deinit is needed, use actor or final class instead of struct (since structs have no deinit). Change stored properties to not use implicitly-unwrapped optional types, and move their initial assignment from setUp to either be initialized inline or, if the initialization is complex, in an initializer.

struct MyTests {
    var fixture = Fixture()
    mutating func `Fixture behaves as expected`() {
        #expect(fixture.doSomething())
    }
}

Avoid pulling instance variables into function bodies; this can cause noise. Swift Testing reinvokes the initializer fresh before each test runs. If the test mutates an instance variable with value semantics, you may need to mark the test function mutating.

Test Methods

Replace the test name prefix with the @Test attribute. If the resulting test name includes multiple camelCase words, use a raw identifier with the test name in sentence case.

  • func testEngineDoesNotStall() { ... } -> @Test func Engine does not stall() { ... }
  • func testIgnition() { ... } -> @Test func ignition() { ... }

Test functions can be async, throws, or async throws, and can be isolated to a global actor with @MainActor.

Assertions to Expectations

When migrating a test from XCTest to Swift Testing, apply these mappings:

XCTAssert(x), XCTAssertTrue(x) -> #expect(x) XCTAssertFalse(x) -> #expect(!x) XCTAssertNil(x) -> #expect(x == nil) XCTAssertNotNil(x) -> #expect(x != nil) XCTAssertEqual(x, y) -> #expect(x == y) XCTAssertNotEqual(x, y) -> #expect(x != y) XCTAssertIdentical(x, y) -> #expect(x === y) XCTAssertNotIdentical(x, y) -> #expect(x !== y) XCTAssertGreaterThan(x, y) -> #expect(x > y) XCTAssertGreaterThanOrEqual(x, y) -> #expect(x >= y) XCTAssertLessThanOrEqual(x, y) -> #expect(x <= y) XCTAssertLessThan(x, y) -> #expect(x < y) try XCTUnwrap(x) -> try #require(x)

There is no direct equivalent for XCTAssertEqual(_:_:accuracy:); use floating point math directly.

Errors

When the error type is Equatable and the exact value is known, prefer to check the specific error value.

XCTAssertThrowsError(try f())

->

#expect(throws: (any Error).self) {
    try f()
}
XCTAssertThrowsError(try f()) { error in
    XCTAssertEqual(error, specificError)
}

->

#expect(throws: specificError) {
    try f()
}
XCTAssertThrowsError(try f()) { error in
    // Check error
}

->

let error = #expect(throws: (any Error).self) {
    try f()
}
// Check error
XCTAssertNoThrow(try f())

->

#expect(throws: Never.self) {
    try f()
}

continueAfterFailure

By default continueAfterFailure is true, which means expectations do not halt the test run. Some XCTestCases set continueAfterFailure = false, which means the XCTAssert family of functions will throw Objective-C exceptions that halt the test execution.

When a test method sets continueAfterFailure = false, all subsequent assertions need to be try #require(x) instead of #expect(x) to preserve this behavior. When adding try #require(x), add throws to the affected methods.

When continueAfterFailure = false is set in setUp, the conversion to try #require(x) must apply to all assertions in all test methods in that class.

Promote Issue.record/XCTFail to expectations

Wherever it is not disruptive, convert usage of Issue.record or XCTFail to #expect or #require, depending if the test exits after (taking continueAfterFailure into account).

In some cases, the source of the expectation itself is sufficient to explain the failure, and the comment would be redundant.

For example, the following structures should be converted as such:

guard let object = somethingOptional() else {
    Issue.record("Could not get object")
    return
}

guard object.isAvailable() else {
    Issue.record("Object not available")
    return
}

if !object.performOperation() {
    Issue.record("Failed to perform operation")
}

->

let object = try #require(somethingOptional(), "Could not get object")
try #require(object.isAvailable())
#expect(object.performOperation())

Asynchronous Expectations to Confirmations

Replace XCTestExpectation + fulfill() + await fulfillment(of:) with confirmation():

// Before
let exp = expectation(description: "...")
handler = { exp.fulfill() }
doWork()
await fulfillment(of: [exp])

// After
await confirmation("...") { confirm in
    handler = { confirm() }
    doWork()
}

For assertForOverFulfill = false with an expectedFulfillmentCount, use a range: await confirmation("...", expectedCount: 10...) { confirm in ... }

Skipping Tests

Replace XCTSkipIf/XCTSkipUnless with traits on the test or suite:

  • try XCTSkipIf(condition) -> @Test(.disabled(if: condition))
  • try XCTSkipUnless(condition) -> @Test(.enabled(if: condition))

Replace throw XCTSkip("reason") mid-test with try Test.cancel("reason").

When a skip checks OS version or platform availability, replace it with an @available attribute on the test function instead of .enabled(if:).

Known Issues

Replace XCTExpectFailure("...", ...) { ... } with withKnownIssue("...") { ... }.

For intermittent failures, replace .nonStrict() option (or the shorthand strict: false parameter) with isIntermittent: true.

For conditional/matching: use when: and matching: parameters:

withKnownIssue("...") {
    try riskyOperation()
} when: {
    shouldExpectFailure
} matching: { issue in
    issue.error != nil
}

Concurrency and Serial Execution

XCTest runs synchronous tests on the main actor and sequentially within a suite by default. Swift Testing runs all test functions on an arbitrary task and in parallel. Add @MainActor only if a test explicitly relied on main-actor isolation in its XCTest form, and add @Suite(.serialized) if tests depend on shared state.

Attachments

Replace XCTAttachment + self.add(attachment) with Attachment.record(value). The attached type must conform to Attachable (automatic for Codable and NSSecureCoding types when Foundation is imported).

Modernization Guidelines

  • When migrating from XCTest, migrate one test class at a time. A file can contain both XCTest and Swift Testing tests during migration.
  • Prefer struct for suites unless deinit (tearDown) is needed, in which case use actor or final class.
  • Remove the test prefix from method names when adding @Test. For lengthier test names which read like a sentence, use raw identifier syntax to improve readability, e.g. @Test func Authenticate, fetch summary, then check count() { ... }.
  • Also check existing @Test functions for multi-word camelCase names and convert those to sentence-case raw identifiers.
  • Use raw identifier syntax only for multi-word names that read like a sentence.
  • When migrating setUp, convert implicitly-unwrapped optional properties to non-optional properties initialized in-place, or in init if initialization is complex, may throw, or is async.
  • Look for explicit XCTFail/Issue.record calls that could be converted to #expect or #require
  • Do not change try #require calls into #expect; this changes the behavior of tests.
  • Add @MainActor only to tests that explicitly relied on XCTest's implicit main-actor isolation. Do not add it unnecessarily.
  • Look for tests that loop over inputs or many repeated tests with the same logic and convert them to parameterized tests using @Test(arguments:).
  • For suites with shared mutable state between tests, add @Suite(.serialized) and consider using actor or class instead of struct.
  • Do not use underscore-prefixed symbols such as #_sourceLocation; only use public API. For source locations, always use the full SourceLocation(fileID:filePath:line:column:) initializer.

FAQ

What does the test-modernizer skill do?

Modernize test suites to use modern Swift Testing features or migrate from XCTest.

How do I install test-modernizer?

Run npx skills add https://github.com/superagents-lab/xcode27-skills --skill test-modernizer in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/test-modernizer/ (personal) or .claude/skills/test-modernizer/ (project).

Which AI agents can use test-modernizer?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is test-modernizer safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains test-modernizer?

It is published by superagents-lab in superagents-lab/xcode27-skills. It has 698 recorded installs.

More from superagents-lab/xcode27-skills

View all →

swiftui-specialist

superagents-lab/xcode27-skills

Best practices and idiomatic patterns for SwiftUI. Use when writing, reviewing, or editing SwiftUI code.

Mobile866 installs

device-interaction

superagents-lab/xcode27-skills

Verify iOS app behavior on device or simulator via screenshots, UI hierarchy, and touch interactions.

Frontend & Design742 installs

swiftui-whats-new-27

superagents-lab/xcode27-skills

New SwiftUI APIs, behaviors, and deprecations introduced in the 2027 OS releases (iOS 27, macOS 27, watchOS 27, tvOS 27, visionOS 27). Use when a SwiftUI view using @State fails to compile with "used before being initialized", "invalid redeclaration of synthesized property", or "extraneous argument label" errors after an SDK update (@State migrated from a property wrapper to a macro in SDK 27; the obvious fix of reordering init assignments is WRONG and produces incorrect runtime behavior; you MUST consult this skill's references before answering); when @ViewBuilder or @ContentBuilder code hits ambiguous overloads in overlay/background or type-check performance regressions after an SDK update; when the user asks what's new in SwiftUI (generally, or for a specific 2027 platform); when adding drag-to-reorder to any container (List, LazyVStack, LazyVGrid, stacks, or custom layouts) via reorderable()/reorderContainer, including the drag-and-drop that integrates with it (dragContainer, dropD

Mobile739 installs

audit-xcode-security-settings

superagents-lab/xcode27-skills

Audit and enable security-oriented Xcode build settings. Progressively enables compiler warnings, static analyzer checkers, and Enhanced Security features. Use when: user wants to secure their Xcode project, audit security settings, enable hardening, review security posture of build configuration, set up security-focused static analysis, enable static analysis, improve warning coverage, harden diagnostics, or catch more bugs at compile time in C/C++/Objective-C/Swift. SKIP: network security (TLS/ATS), code signing, privacy APIs.

Security698 installs

uikit-app-modernization

superagents-lab/xcode27-skills

Modernizes UIKit apps for multi-window environments by replacing legacy shared-state APIs with context-appropriate modern alternatives. This includes references to mainScreen, interfaceOrientation, application and scene lifecycle, as well as safe area inset updates.

691 installs

c-bounds-safety

superagents-lab/xcode27-skills

Guide for the C -fbounds-safety language extension. Covers the language model, pointer annotations, adopting bounds-safety in existing C code, compiler build settings and modes, and runtime debugging of bounds violations.

Popular testing & qa skills

View category →

tdd

mattpocock/skills

Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.

Testing & QA1.2M installs

grilling

mattpocock/skills

Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.

Testing & QA996.2K installs

migrate-to-shoehorn

mattpocock/skills

Migrate test files from as type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace as in tests, or needs partial test data.

Testing & QA484.1K installs

run-train

lllllllama/rigorpilot-skills

Rigor Train skill for deep learning research repositories. Use when a documented or selected training command should be run conservatively for startup verification, short-run verification, full kickoff, or resume, with command, config, seed, log, checkpoint, status, and metric evidence written to standardized trainoutputs/. Do not use for environment setup, exploratory sweeps, speculative idea implementation, or end-to-end orchestration.

Testing & QA337.8K installs

test-driven-development

obra/superpowers

Use when implementing any feature or bugfix, before writing implementation code

Testing & QA261.8K installs

Use when implementation is complete, all tests pass, and you need to decide how to integrate the work

Testing & QA216.8K installs