Pour qu’Xcode mette en évidence le test qui a appelé un assistant XCTest partagé, donnez à l’assistant des paramètres #filePath et #line par défaut et transmettez-les à ses assertions. Ce tutoriel présente la correction, les assistants imbriqués et l’équivalent avec Swift Testing.

Pourquoi Xcode met-il l’assistant en évidence ?

Une assertion partagée introduit un autre emplacement dans le code source. Le test appelle votre assistant, puis l’assistant appelle une assertion XCTest. Si cette assertion utilise les arguments d’emplacement par défaut, l’emplacement signalé est celui de l’assertion dans l’assistant. Apple documente file: StaticString = #filePath et line: UInt = #line à la fois pour XCTAssertEqual et XCTFail.

Capturez l’emplacement de l’appelant à la limite de l’assistant, puis transmettez ces valeurs. Les valeurs par défaut de l’emplacement source de Swift capturent le site d’appel ; le projet Swift explique ce comportement dans SE-0274 : noms de fichiers magiques concis. Apple présente également ce modèle exact d’assistant XCTest dans Migrer vers Swift Testing, en montrant qu’Xcode attribue l’échec à l’appel de l’assistant.

Pour un développeur qui travaille dans Xcode sur Mac, l’objectif concret est de conserver des vérifications réutilisables tout en faisant en sorte que chaque échec renvoie à l’entrée de test pertinente. Les exemples ci-dessous utilisent uniquement de petits tableaux d’entiers. Lorsqu’ils sont exécutés, ils ne nécessitent ni modèle d’application, ni substitut de service, ni dépendance téléchargée, ni accès réseau.

Ajouter des paramètres d’emplacement de l’appelant à un assistant XCTest

Utilisez une cible de test unitaire macOS existante. Si le projet en a besoin, choisissez File > New > Target, filtrez sur Test, puis sélectionnez Unit Testing Bundle. Pour ajouter un fichier de test, choisissez File > New > New File From Template et sélectionnez XCTest Unit Test. Apple documente ces chemins dans Ajouter des tests à votre projet Xcode.

Placez ce fichier de test complet dans cette cible de test unitaire. Le deuxième test fournit volontairement un total attendu incorrect afin que vous puissiez examiner l’emplacement de l’échec.

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

Les éléments importants sont les types des paramètres, leurs valeurs par défaut et leur transmission explicite. StaticString et UInt correspondent à la signature de l’assertion XCTest. Gardez #filePath et #line directement dans les valeurs par défaut de l’assistant. Le test appelle alors l’assistant sans fournir manuellement les arguments d’emplacement. Consultez les paramètres documentés de XCTAssertEqual.

Le calcul est donné à titre d’illustration : 2 + 3 + 4 vaut 9, donc la première comparaison correspond et la deuxième ne correspond pas. Ces calculs ont été vérifiés localement avec Python. Aucun exécutable Swift n’était disponible dans l’environnement de recherche ; les exemples Swift ont donc été examinés, mais n’ont pas été compilés ni exécutés ici.

Vérifier l’emplacement et le résultat du test

Choisissez Product > Test ou exécutez le test individuellement à l’aide du bouton d’exécution situé à côté de sa déclaration. Ensuite, sélectionnez le test en échec dans le Test navigator et examinez le diagnostic. Apple décrit ces commandes et les détails des échecs dans Exécuter les tests et interpréter les résultats.

Dans cet exemple, vérifiez deux résultats distincts : testDeliberatelyIncorrectTotal() échoue et le diagnostic de son assertion pointe vers l’appel assertTotal([2, 3, 4], equals: 10). L’attribution à l’appelant attendue suit le modèle de transmission présenté par Apple. Après l’avoir vérifiée, remplacez 10 par 9 et relancez les tests ; l’incohérence intentionnelle doit être supprimée de la suite de tests finale.

Si le diagnostic pointe toujours à l’intérieur de l’assistant, examinez l’appel final à l’assertion. Déclarer des paramètres d’emplacement ne suffit pas si l’assistant omet file: file ou line: line à l’endroit où l’échec est enregistré.

Préserver l’emplacement à chaque niveau d’assistant

Les assistants imbriqués nécessitent la même transmission explicite. Supposons qu’un test appelle un assistant métier, qui appelle ensuite un assistant d’assertion de niveau inférieur. Si l’assistant externe omet les arguments d’emplacement, l’assistant de niveau inférieur capture le site de cet appel intermédiaire.

Il s’agit d’une limite du langage à vérifier : les valeurs par défaut de l’emplacement source ne sont pas automatiquement transmises lors d’appels arbitraires entre assistants. SE-0285 : faciliter la transition vers des chaînes de fichiers magiques concises aborde explicitement le caractère non transitif de ces valeurs par défaut.

Voici un autre fichier de test complet. Il montre un assistant qui en appelle un autre tout en préservant l’emplacement du test d’origine.

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

Pour examiner une chaîne d’assistants plus longue, faites un simple exercice de suivi : partez de l’appel du test, suivez chaque assistant et vérifiez que le même emplacement capturé parvient jusqu’à l’assertion. Capturez les valeurs par défaut à la limite qui doit apparaître dans le résultat ; transmettez les valeurs existantes aux autres niveaux. Évitez de masquer la capture derrière une autre fonction utilisée comme argument par défaut, écueil illustré par SE-0285.

Le signalement d’un échec arrête-t-il le test appelant ?

L’attribution de l’échec et le flux de contrôle sont deux décisions distinctes. Dans XCTest, continueAfterFailure vaut true par défaut ; une assertion en échec permet donc normalement à l’exécution de continuer. Modifier le fichier et la ligne signalés ne change pas cette propriété. Apple documente ce comportement dans continueAfterFailure.

Si l’assistant détecte une condition qui invalide le reste de son propre travail, donnez-lui un chemin de sortie explicite. Par exemple, enregistrez l’échec en transmettant l’emplacement, puis quittez l’assistant avec return. N’oubliez pas que quitter un assistant rend la main à son appelant ; concevez la suite du travail de l’appelant en conséquence. Le guide de migration d’Apple illustre un échec inconditionnel suivi d’un retour.

Effectuez la première vérification de façon synchrone et simple. Vérifiez qu’un calcul volontairement incorrect échoue au site d’appel attendu avant d’ajouter une configuration plus élaborée ou un comportement asynchrone. Cette séquence permet de distinguer un défaut de transmission de l’emplacement d’autres problèmes d’exécution des tests.

Utiliser un emplacement source pris en charge dans Swift Testing

Pour un assistant appelé par des tests Swift Testing, utilisez #expect avec un SourceLocation explicite. Apple documente sourceLocation comme l’emplacement auquel les attentes et les problèmes sont attribués, et indique qu’une condition fausse enregistre un problème pour le test exécuté dans la tâche en cours. Consultez expect(_:_:sourceLocation:).

Ce fichier alternatif complet nécessite Swift Testing, disponible avec Xcode 16 et Swift 6.0. Il capture les quatre composantes de l’emplacement comme valeurs par défaut distinctes, puis construit l’initialiseur documenté de 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)
}

L’initialiseur exige un identifiant de fichier non vide et correctement formaté, ainsi que des numéros de ligne et de colonne positifs. La capture des composantes fournies par le compilateur évite d’inventer ces valeurs. Lorsque vous imbriquez ces assistants, transmettez les quatre arguments capturés, comme la version XCTest transmet ses deux arguments. Là encore, corrigez l’attente délibérément incorrecte 10 après avoir vérifié l’attribution.

Qu’est-ce qui change lorsqu’un assistant est partagé entre XCTest et Swift Testing ?

Les deux frameworks peuvent coexister dans un même bundle de tests, mais un assistant qui franchit la frontière entre ces frameworks nécessite une gestion adaptée à la version. Le guide de migration actuel d’Apple documente l’interopérabilité et ses valeurs par défaut : les chaînes d’outils antérieures à Swift 6.4 utilisent none ; Swift 6.4 ou une version ultérieure utilise par défaut limited pour les packages qui déclarent une version antérieure des outils et complete pour ceux qui déclarent la version 6.4 ou ultérieure.

Mode d’interopérabilité Échec d’une assertion XCTest dans un test Swift Testing
none Le problème entre bibliothèques est ignoré
limited Signalé sous forme d’avertissement, avec des conseils de modernisation
complete Conserve le niveau de gravité de l’échec, avec des conseils de modernisation
strict Signalé au moyen de fatalError

Ces modes expliquent pourquoi un assistant peut signaler le bon texte tout en laissant un test Swift Testing réussir. La session de migration WWDC26 d’Apple montre précisément ce résultat, puis modifie le paramètre d’interopérabilité du plan de test sous Test Execution pour le régler sur Complete et préserver le niveau de gravité de l’échec. Vérifiez la chaîne d’outils installée avant de vous fier à cette fonctionnalité.

Avec les chaînes d’outils plus anciennes, gardez le signalement des assertions propre à chaque framework et partagez le calcul déterministe sous-jacent. Pour les configurations d’interopérabilité prises en charge, Apple montre également comment remplacer XCTFail dans un assistant par Issue.record de Swift Testing. La transmission de l’emplacement source doit toujours être préservée pendant cette migration.

Diagnostiquer le symptôme restant

Ce que vous observez Élément à examiner ensuite
L’échec pointe à l’intérieur de l’assistant d’assertion Vérifiez que l’assertion reçoit les arguments d’emplacement capturés
L’échec pointe à l’intérieur d’un assistant externe Vérifiez que cet assistant transmet l’emplacement de son appelant
Le test Swift Testing réussit malgré une assertion XCTest Vérifiez la prise en charge de la chaîne d’outils et le mode d’interopérabilité
Le code s’exécute après l’échec d’une assertion XCTest Vérifiez le flux de contrôle voulu et continueAfterFailure
Aucun exemple de test n’apparaît lors de l’exécution Vérifiez que le fichier appartient à la cible de test unitaire et que le plan de test actif l’inclut

Pour la découverte et la sélection des tests, le guide de configuration des tests et le guide d’exécution des tests d’Apple décrivent les cibles de test et les plans actifs. Pour un passage entre frameworks, appliquez les règles d’interopérabilité documentées ci-dessus avant de modifier les arguments d’emplacement.

Un assistant partagé utile préserve trois éléments : une comparaison pertinente, un diagnostic lié à l’appelant visé et le résultat de test attendu. Vérifiez-les à l’aide d’une incohérence délibérée, supprimez cette incohérence et continuez à transmettre l’emplacement capturé chaque fois que vous ajoutez un niveau d’assistant.

Sources