Aby Xcode wskazywał test, który wywołał współdzieloną funkcję pomocniczą XCTest, nadaj jej domyślne parametry #filePath i #line, a następnie przekaż oba do asercji. Ten przewodnik pokazuje poprawkę, zagnieżdżone funkcje pomocnicze i odpowiednik w Swift Testing.

Dlaczego Xcode wskazuje funkcję pomocniczą?

Współdzielona asercja wprowadza dodatkowe miejsce w kodzie źródłowym. Test wywołuje funkcję pomocniczą, a ona wywołuje asercję XCTest. Jeśli ta asercja korzysta z domyślnych argumentów określających lokalizację, zgłoszona lokalizacja wskazuje asercję wewnątrz funkcji pomocniczej. Apple dokumentuje parametry file: StaticString = #filePath i line: UInt = #line zarówno w XCTAssertEqual, jak i w XCTFail.

Przechwyć lokalizację wywołującego na granicy funkcji pomocniczej, a następnie przekaż te wartości dalej. Domyślne wartości lokalizacji w Swift przechwytują miejsce wywołania; projekt Swift wyjaśnia to działanie w dokumencie SE-0274: Concise magic file names. Apple pokazuje dokładnie ten wzorzec funkcji pomocniczej XCTest w artykule Migrate to Swift Testing, demonstrując, że Xcode przypisuje błąd do wywołania funkcji pomocniczej.

Dla programisty pracującego w Xcode na Macu konkretne zadanie polega na zachowaniu wielokrotnego użytku kontroli przy jednoczesnym kierowaniu każdego błędu z powrotem do odpowiednich danych wejściowych testu. Poniższe przykłady używają wyłącznie małych tablic liczb całkowitych. Nie wymagają modelu aplikacji, zamiennika usługi, pobieranej zależności ani dostępu do sieci podczas uruchamiania.

Dodaj parametry lokalizacji wywołującego do funkcji pomocniczej XCTest

Użyj istniejącego docelowego elementu testów jednostkowych dla macOS. Jeśli projekt go nie ma, wybierz File > New > Target, przefiltruj wyniki według Test i wybierz Unit Testing Bundle. Aby dodać plik testowy, wybierz File > New > New File From Template i wybierz XCTest Unit Test. Są to ścieżki opisane przez Apple w artykule Adding tests to your Xcode project.

Umieść ten kompletny plik testowy w docelowym elemencie testów jednostkowych. Drugi test celowo podaje nieprawidłową oczekiwaną sumę, aby można było sprawdzić lokalizację błędu.

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)
    }
}

Najważniejsze elementy to typy parametrów, wartości domyślne i jawne przekazywanie argumentów. StaticString i UInt odpowiadają sygnaturze asercji XCTest. Umieść #filePath i #line bezpośrednio w wartościach domyślnych funkcji pomocniczej. Test wywołuje ją wtedy bez ręcznego podawania któregokolwiek z argumentów lokalizacji. Zobacz udokumentowane parametry XCTAssertEqual.

Obliczenia mają charakter ilustracyjny: 2 + 3 + 4 to 9, więc pierwsze porównanie jest zgodne, a drugie nie. Te przypadki arytmetyczne sprawdzono lokalnie w Pythonie. W środowisku badawczym nie było dostępnego pliku wykonywalnego Swifta, dlatego przykłady w Swift zostały przejrzane, ale nie skompilowano ich ani nie uruchomiono.

Sprawdź lokalizację i wynik testu

Wybierz Product > Test albo uruchom pojedynczy test przyciskiem odtwarzania obok jego deklaracji. Następnie zaznacz nieudany test w Test navigator i sprawdź diagnostykę. Apple opisuje te elementy sterujące i szczegóły błędów w artykule Running tests and interpreting results.

W tym przykładzie sprawdź dwa osobne wyniki: testDeliberatelyIncorrectTotal() kończy się niepowodzeniem, a diagnostyka asercji wskazuje wywołanie assertTotal([2, 3, 4], equals: 10). Oczekiwane przypisanie do wywołującego wynika ze wzorca przekazywania argumentów pokazanego przez Apple. Po sprawdzeniu zmień 10 na 9 i ponownie uruchom testy; zamierzona niezgodność powinna zostać usunięta z ukończonego zestawu testów.

Jeśli diagnostyka nadal wskazuje wnętrze funkcji pomocniczej, sprawdź końcowe wywołanie asercji. Samo zadeklarowanie parametrów lokalizacji nie wystarczy, jeśli funkcja pomocnicza pomija file: file lub line: line w miejscu rejestrowania błędu.

Zachowaj lokalizację na każdym poziomie funkcji pomocniczych

Zagnieżdżone funkcje pomocnicze wymagają takiego samego jawnego przekazywania argumentów. Załóżmy, że test wywołuje funkcję pomocniczą związaną z domeną, a ta wywołuje funkcję pomocniczą niższego poziomu, która wykonuje asercję. Jeśli funkcja zewnętrzna pominie argumenty lokalizacji, funkcja niższego poziomu przechwyci miejsce tego pośredniego wywołania.

To granica języka, którą warto sprawdzić: domyślne wartości lokalizacji źródła nie są automatycznie przekazywane przez dowolne wywołania funkcji pomocniczych. Dokument SE-0285: Ease the transition to concise magic file strings wyraźnie omawia nieprzechodnie działanie tych wartości domyślnych.

Poniżej znajduje się kompletny alternatywny plik testowy. Pokazuje on, jak jedna funkcja pomocnicza wywołuje drugą, zachowując lokalizację pierwotnego testu.

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)
    }
}

Przeglądając dłuższy łańcuch funkcji pomocniczych, wykonaj proste ćwiczenie: zacznij od wywołania w teście, prześledź każdą funkcję pomocniczą i sprawdź, czy ta sama przechwycona lokalizacja dociera do asercji. Przechwytuj wartości domyślne na granicy, która ma pojawić się w wyniku; przez pozostałe warstwy przekazuj istniejące wartości. Unikaj ukrywania przechwytywania w innej funkcji używanej jako argument domyślny — to pułapka zilustrowana w SE-0285.

Czy zgłoszenie błędu zatrzymuje wywołujący test?

Przypisanie błędu i przepływ sterowania to odrębne kwestie. continueAfterFailure w XCTest domyślnie ma wartość true, więc po nieudanej asercji wykonywanie zwykle jest kontynuowane. Zmiana zgłaszanych pliku i wiersza nie wpływa na tę właściwość. Apple dokumentuje to działanie w continueAfterFailure.

Jeśli funkcja pomocnicza wykryje warunek, który sprawia, że jej dalsze działanie jest nieprawidłowe, zapewnij jej jawną ścieżkę wyjścia. Możesz na przykład zarejestrować błąd z przekazaną lokalizacją, a następnie zwrócić sterowanie z funkcji pomocniczej. Pamiętaj, że powrót z funkcji pomocniczej przekazuje sterowanie z powrotem do jej wywołującego; odpowiednio zaplanuj dalsze działanie wywołującego. Przewodnik migracji Apple pokazuje bezwarunkowe zgłoszenie błędu, po którym następuje powrót.

Pierwszą weryfikację wykonaj synchronicznie i w małej skali. Najpierw sprawdź, czy celowo nieprawidłowe obliczenie kończy się niepowodzeniem we wskazanym miejscu wywołania, a dopiero potem dodawaj bardziej rozbudowane przygotowanie lub zachowanie asynchroniczne. Taka kolejność pozwala odróżnić problem z przekazywaniem lokalizacji od innych problemów z wykonywaniem testów.

Użyj obsługiwanej lokalizacji źródłowej w Swift Testing

W przypadku funkcji pomocniczej wywoływanej przez testy Swift Testing użyj #expect z jawnym SourceLocation. Apple dokumentuje sourceLocation jako lokalizację, do której przypisuje się oczekiwania i zgłoszenia, oraz stwierdza, że warunek o wartości false rejestruje zgłoszenie dla testu wykonywanego w bieżącym zadaniu. Zobacz expect(_:_:sourceLocation:).

Ten kompletny alternatywny plik wymaga Swift Testing, dostępnego w Xcode 16 i Swift 6.0. Przechwytuje cztery składowe lokalizacji jako osobne wartości domyślne, a następnie konstruuje udokumentowaną wartość za pomocą inicjalizatora SourceLocation.

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)
}

Inicjalizator wymaga prawidłowo sformatowanego, niepustego identyfikatora pliku oraz dodatnich numerów wiersza i kolumny. Przechwycenie składowych dostarczanych przez kompilator pozwala uniknąć wymyślania tych wartości. Przy zagnieżdżaniu tych funkcji pomocniczych przekaż wszystkie cztery przechwycone argumenty, tak jak wersja XCTest przekazuje swoje dwa argumenty. Po sprawdzeniu przypisania popraw także celowo nieprawidłowe oczekiwanie 10.

Co się zmienia, gdy XCTest i Swift Testing współdzielą funkcję pomocniczą?

Oba frameworki mogą współistnieć w jednym pakiecie testowym, ale funkcja pomocnicza przekraczająca granice frameworków wymaga obsługi zależnej od wersji. Aktualny przewodnik migracji Apple opisuje współdziałanie i jego wartości domyślne: łańcuchy narzędzi wcześniejsze niż Swift 6.4 używają none; Swift 6.4 lub nowszy domyślnie wybiera limited dla pakietów deklarujących wcześniejszą wersję narzędzi oraz complete dla pakietów deklarujących wersję narzędzi 6.4 lub nowszą.

Tryb współdziałania Błąd asercji XCTest wewnątrz testu Swift Testing
none Zgłoszenie z drugiej biblioteki jest ignorowane
limited Zgłaszane jako ostrzeżenie z wskazówkami dotyczącymi modernizacji
complete Zachowuje poziom błędu i zawiera wskazówki dotyczące modernizacji
strict Zgłaszane za pomocą fatalError

Te tryby wyjaśniają, dlaczego funkcja pomocnicza może zgłosić właściwy tekst, a mimo to test Swift Testing może zakończyć się powodzeniem. Sesja migracyjna WWDC26 Apple pokazuje dokładnie taki wynik, a następnie zmienia ustawienie współdziałania planu testów w sekcji Test Execution na Complete, aby zachować poziom błędu. Zanim zaczniesz polegać na tej funkcji, sprawdź zainstalowany łańcuch narzędzi.

W starszych łańcuchach narzędzi zgłaszaj asercje natywnie dla każdego frameworka, a współdziel jedynie deterministyczne obliczenia wykonywane pod spodem. W obsługiwanych konfiguracjach współdziałania Apple pokazuje również, jak zastąpić XCTFail w funkcji pomocniczej przez Issue.record z Swift Testing. Podczas tej migracji nadal trzeba zachować przekazywanie lokalizacji źródłowej.

Zdiagnozuj pozostały problem

Co obserwujesz Co sprawdzić w następnej kolejności
Błąd wskazuje wnętrze funkcji pomocniczej z asercją Sprawdź, czy asercja otrzymuje przechwycone argumenty lokalizacji
Błąd wskazuje wnętrze funkcji zewnętrznej Sprawdź, czy ta funkcja przekazuje lokalizację wywołującego
Test Swift Testing kończy się powodzeniem mimo asercji XCTest Sprawdź obsługę w łańcuchu narzędzi i tryb współdziałania
Kod wykonuje się dalej po nieudanej asercji XCTest Sprawdź zamierzony przepływ sterowania i continueAfterFailure
Przykładowy test nie pojawia się podczas uruchamiania Sprawdź, czy plik należy do docelowego elementu testów jednostkowych i czy uwzględnia go aktywny plan testów

W kwestii wykrywania i wybierania testów przewodniki Apple test setup guide i test execution guide opisują docelowe elementy testów oraz aktywne plany. Przy testowaniu między frameworkami przed zmianą argumentów lokalizacji zastosuj opisane powyżej zasady współdziałania.

Przydatna współdzielona funkcja pomocnicza zachowuje trzy rzeczy: znaczące porównanie, diagnostykę powiązaną z zamierzonym wywołującym oraz oczekiwany wynik testu. Sprawdź je za pomocą jednej celowo wprowadzonej niezgodności, usuń ją, a gdy dodasz kolejną warstwę funkcji pomocniczej, nadal przekazuj przechwyconą lokalizację.

Źródła