XCUITest
Add the TraceraXCUITest dependency from tracera-swift to your UI test target in Package.swift (it pulls in Tracera):
dependencies: [
.package(url: "https://github.com/tracera-dev/tracera-swift", from: "0.1.0"),
],
targets: [
.testTarget(
name: "MyAppUITests",
dependencies: [
.product(name: "TraceraXCUITest", package: "tracera-swift"),
]
),
]
Requires Swift ≥ 6, macOS ≥ 15 (or iOS ≥ 18), and Xcode ≥ 16 (see Autotest adapters).
Environment variables and run mode: Autotest adapters.
Subclass TraceraXCTestCase and call install() once in class func setUp() (media defaults off — see Screenshots):
import XCTest
import Tracera
import TraceraXCUITest
final class CheckoutUITests: TraceraXCTestCase {
override class func setUp() {
install()
}
// …
}
When you override instance setUp / tearDown (once per test — not class func setUp), call super.setUp() at the end of setUp and super.tearDown() at the start of tearDown so steps land in Setup / Test / Teardown.
Bind testCaseId and autotestName
Bind with Tracera.testCase at the start of each test method:
func testCheckout() {
Tracera.testCase(42)
// Tracera.testCase(42, "e2e/checkout")
// …
}
If name is omitted, autotestName takes the value of the XCTest test name. You can bind in instance setUp instead when several methods share one case id.
Skip
Use skip("reason") so the reason is added to the result as comment:
func testCheckout() throws {
Tracera.testCase(42)
try skip("needs a seeded account")
}
Steps
Use Tracera.step("…") { } or await Tracera.step("…") { }. Steps inside instance setUp / tearDown go to the result’s Setup / Teardown; steps inside the test go to Test.
override func setUp() {
Tracera.step("launch app") { }
super.setUp()
}
func testCheckout() throws {
Tracera.testCase(10)
try Tracera.step("tap checkout") {
try Tracera.step("nested leaf") { }
}
}
override func tearDown() {
super.tearDown()
Tracera.step("terminate app") { }
}
Comments, errors, and attachments
Limits and allowed MIME types: Autotest adapters.
Tracera.comment, Tracera.error, and attachments are added to the open step, or to the result when no step is open. Tracera.attachmentFile takes a file path; Tracera.attachmentBytes takes a name and bytes (MIME type is optional). Attachments upload when the result is reported.
Tracera.comment("result-level note")
Tracera.error("result-level error")
Tracera.attachmentFile("Fixtures/screenshot.png")
Tracera.step("open login") {
Tracera.comment("step note")
Tracera.attachmentBytes("body.json", data: Data(#"{"ok":true}"#.utf8), mimeType: "application/json")
}
If you start a Task and call Tracera APIs there (steps, comments, errors, attachments), call getContext() in the test first and run the worker with runWithContext — otherwise those calls are not reported:
let ctx = getContext()
Task {
await runWithContext(ctx) {
Tracera.step("worker") { /* ... */ }
}
}
Parameterized
Use a literal Tracera.testCase(id, name) (or omit name → XCTest test name).
Screenshots and media
install() is required for reporting (see above). Use its parameters to attach media at the end of each test (default .off): screenshots: — .onFailure / .onSuccess / .always (iOS/tvOS: XCUIScreen; macOS: ScreenCaptureKit when screen recording is allowed); hierarchy: → window-hierarchy.txt; logs: → device-log.txt (unified log / OSLogStore).
override class func setUp() {
install(screenshots: .onFailure, hierarchy: .onFailure, logs: .onFailure)
}
To capture a screenshot, hierarchy, or device log at a chosen moment (for example after a tap), call manually:
Tracera.step("after login") {
screenshot()
attachHierarchy()
attachDeviceLog()
}