To make Xcode highlight the test that called a shared XCTest helper, give the helper default #filePath and #line parameters and forward both to its assertions. This walkthrough shows the fix, nested helpers, and the Swift Testing equivalent.
Why does Xcode highlight the helper?
A shared assertion introduces another source location. The test calls your helper, and the helper calls an XCTest assertion. If that assertion uses its default location arguments, its reported location is the assertion inside the helper. Apple documents file: StaticString = #filePath and line: UInt = #line on both XCTAssertEqual and XCTFail.
Capture the caller’s location at the helper’s boundary, then pass those values onward. Swift’s source-location defaults capture the call site; the Swift project explains this behavior in SE-0274: Concise magic file names. Apple also demonstrates this exact XCTest helper pattern in Migrate to Swift Testing, showing Xcode attributing the failure to the helper call.
For a developer working in Xcode on a Mac, the concrete task is to preserve reusable checks while making each failure lead back to the relevant test input. The examples below use only small integer arrays. They require no app model, service substitute, downloaded dependency, or network access when run.
Add caller-location parameters to an XCTest helper
Use an existing macOS unit test target. If the project needs one, choose File > New > Target, filter for Test, and select Unit Testing Bundle. To add a test file, choose File > New > New File From Template and select XCTest Unit Test. These are Apple’s documented paths in Adding tests to your Xcode project.
Place this complete test file in that unit test target. The second test deliberately supplies an incorrect expected total so you can inspect the failure location.
import XCTest
private func assertTotal(
_ values: [Int],
equals expected: Int,
file: StaticString = #filePath,
line: UInt = #line
) {
let actual = values.reduce(0, +)
XCTAssertEqual(
actual,
expected,
"Unexpected total for \(values)",
file: file,
line: line
)
}
final class TotalHelperTests: XCTestCase {
func testMatchingTotal() {
assertTotal([2, 3, 4], equals: 9)
}
func testDeliberatelyIncorrectTotal() {
assertTotal([2, 3, 4], equals: 10)
}
}
The important pieces are the parameter types, the defaults, and the explicit forwarding. StaticString and UInt match XCTest’s assertion signature. Keep #filePath and #line directly in the helper’s defaults. The test then calls the helper without manually supplying either location argument. See the documented XCTAssertEqual parameters.
The arithmetic is illustrative: 2 + 3 + 4 is 9, so the first comparison matches and the second does not. These arithmetic cases were checked locally with Python. A Swift executable was unavailable in the research environment, so the Swift examples were inspected but were not compiled or run here.
Verify the location and the test result
Choose Product > Test, or run the individual test using the play control beside its declaration. Afterward, select the failing test in the Test navigator and inspect the diagnostic. Apple describes these controls and the failure details in Running tests and interpreting results.
For this example, verify two separate results: testDeliberatelyIncorrectTotal() fails, and its assertion diagnostic points to the assertTotal([2, 3, 4], equals: 10) call. The expected caller attribution follows Apple’s demonstrated forwarding pattern. After checking it, change 10 to 9 and rerun the tests; the intentional mismatch should be removed from the finished test suite.
If the diagnostic still points inside the helper, inspect the final assertion call. Declaring location parameters is insufficient when the helper omits file: file or line: line at the point that records the failure.
Preserve the location through every helper layer
Nested helpers need the same explicit forwarding. Suppose a test calls a domain helper, which then calls a lower-level assertion helper. If the outer helper leaves out the location arguments, the lower-level helper captures that intermediate call site.
This is a language boundary worth checking: source-location defaults do not automatically travel through arbitrary helper calls. SE-0285: Ease the transition to concise magic file strings explicitly discusses the nontransitive behavior of these defaults.
The following is a complete alternative test file. It demonstrates a helper calling another helper while preserving the original test location.
import XCTest
private func assertIntegerEqual(
_ actual: Int,
_ expected: Int,
file: StaticString = #filePath,
line: UInt = #line
) {
XCTAssertEqual(actual, expected, file: file, line: line)
}
private func assertRowTotal(
_ row: [Int],
equals expected: Int,
file: StaticString = #filePath,
line: UInt = #line
) {
assertIntegerEqual(
row.reduce(0, +),
expected,
file: file,
line: line
)
}
final class NestedTotalHelperTests: XCTestCase {
func testRowTotal() {
assertRowTotal([2, 3, 4], equals: 9)
}
}
Use a simple tracing exercise when reviewing a larger helper chain: start at the test call, follow each helper, and check that the same captured location reaches the assertion. Capture defaults at the boundary intended to appear in the result; forward existing values through the remaining layers. Avoid hiding capture behind another function used as a default argument, the pitfall illustrated by SE-0285.
Does reporting a failure stop the calling test?
Failure attribution and control flow are separate decisions. XCTest’s continueAfterFailure defaults to true, so a failed assertion normally allows execution to continue. Changing the reported file and line does not change that property. Apple documents this behavior in continueAfterFailure.
If a helper detects a condition that makes its own remaining work invalid, give that helper an explicit exit path. For example, record the failure with the forwarded location and then return from the helper. Remember that returning from a helper returns control to its caller; design the caller’s subsequent work accordingly. Apple’s migration guide illustrates an unconditional failure followed by a return.
Keep the first verification synchronous and small. Establish that an intentionally incorrect calculation fails at the expected call site before adding more elaborate setup or asynchronous behavior. That sequence separates a location-forwarding defect from other test execution problems.
Use a supported source location in Swift Testing
For a helper called by Swift Testing tests, use #expect with an explicit SourceLocation. Apple documents sourceLocation as the location to which expectations and issues are attributed, and states that a false condition records an issue for the test running in the current task. See expect(_:_:sourceLocation:).
This complete alternative file requires Swift Testing, available with Xcode 16 and Swift 6.0. It captures the four location components as separate defaults, then constructs the documented SourceLocation initializer.
import Testing
private func expectTotal(
_ values: [Int],
equals expected: Int,
fileID: String = #fileID,
filePath: String = #filePath,
line: Int = #line,
column: Int = #column
) {
let location = SourceLocation(
fileID: fileID,
filePath: filePath,
line: line,
column: column
)
let actual = values.reduce(0, +)
#expect(actual == expected, sourceLocation: location)
}
@Test func matchingTotal() {
expectTotal([2, 3, 4], equals: 9)
}
@Test func deliberatelyIncorrectTotal() {
expectTotal([2, 3, 4], equals: 10)
}
The initializer requires a properly formatted, nonempty file ID and positive line and column numbers. Capturing the compiler-provided components avoids inventing those values. When nesting these helpers, forward all four captured arguments, just as the XCTest version forwards its two arguments. Again, correct the deliberate 10 expectation after verifying attribution.
What changes when XCTest and Swift Testing share a helper?
Both frameworks can coexist in one test bundle, but a helper that crosses framework boundaries needs version-aware handling. Apple’s current migration guide documents interoperability and its defaults: toolchains before Swift 6.4 use none; Swift 6.4 or later defaults to limited for packages declaring an earlier tools version and complete for packages declaring tools version 6.4 or later.
| Interoperability mode | XCTest assertion failure inside a Swift Testing test |
|---|---|
none |
Cross-library issue is ignored |
limited |
Reported as a warning, with modernization guidance |
complete |
Retains failure severity, with modernization guidance |
strict |
Reported using fatalError |
Those modes explain why a helper can report the right text yet leave a Swift Testing test passing. Apple’s WWDC26 migration session shows this exact outcome, then changes the test plan’s interoperability setting under Test Execution to Complete to preserve failure severity. Check your installed toolchain before relying on that feature.
For older toolchains, keep assertion reporting native to each framework and share the deterministic calculation underneath. For supported interoperability configurations, Apple also demonstrates replacing a helper’s XCTFail with Swift Testing’s Issue.record. Source-location forwarding still needs to be preserved during that migration.
Diagnose the remaining symptom
| What you observe | What to inspect next |
|---|---|
| Failure points inside the assertion helper | Confirm the assertion receives the captured location arguments |
| Failure points inside an outer helper | Confirm that outer helper forwards its caller’s location |
| Swift Testing test passes despite an XCTest assertion | Check toolchain support and interoperability mode |
| Code runs after an XCTest assertion fails | Check the intended control flow and continueAfterFailure |
| No example test appears in the run | Confirm the file belongs to the unit test target and the active test plan includes it |
For test discovery and selection, Apple’s test setup guide and test execution guide describe test targets and active plans. For a cross-framework pass, use the documented interoperability rules above before changing location arguments.
A useful shared helper preserves three things: a meaningful comparison, a diagnostic tied to the intended caller, and the intended test outcome. Verify them with one deliberate mismatch, remove that mismatch, and keep forwarding the captured location whenever another helper layer is introduced.
Sources
- XCTAssertEqual(::_:file:line:) — Apple
- XCTFail(_:file:line:) — Apple
- Adding tests to your Xcode project — Apple
- Running tests and interpreting results — Apple
- continueAfterFailure — Apple
- Migrating a test from XCTest — Apple
- expect(::sourceLocation:) — Apple
- init(fileID:filePath:line:column:) — Apple
- Migrate to Swift Testing — Apple
- Concise magic file names — Swift Project
- Ease the transition to concise magic file strings — Swift Project