Skip to content

XCTest, XCUITest, test plans, and framework coexistence

XCTest remains the right tool for XCUITest interface automation, performance measurement, Objective-C interoperability, and established suites whose rewrite would add cost without stronger evidence.

Give one UI journey a controlled launch

final class CreateNoteUITests: XCTestCase {
func testCreatesNoteFromEmptyLibrary() {
let app = XCUIApplication()
app.launchArguments = ["--ui-testing", "--fixture", "empty-library"]
app.launch()
app.buttons["New note"].tap()
app.textFields["Title"].tap()
app.textFields["Title"].typeText("Trail marker")
app.buttons["Save"].tap()
XCTAssertTrue(app.staticTexts["Trail marker"].waitForExistence(timeout: 2))
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.lifetime = .keepAlways
add(attachment)
}
}

The app must interpret test arguments only in a controlled nonproduction path and load deterministic local data. Stable visible labels are preferable selectors; accessibility identifiers help when labels are localized or duplicated.

Use test plans for matrices

A plan groups configurations, environment settings, language and region choices, repetitions, diagnostics, and target selection. Keep a fast pull-request plan and broader release plan rather than making every developer run every destination.

Let frameworks coexist

New domain tests can use Swift Testing while XCUITest and performance suites stay in XCTest. Shared production fixtures and helpers should not depend on either framework. Migrate only when readability, diagnostics, parallelism, or maintenance materially improve.

Validation boundary

The UI journey requires a generated Xcode project, matching scheme, and Simulator destination. It is not marked as executed here.

Series navigation

References