XCTest est un framework Apple pour les tests unitaires et d'intégration d'applications pour iOS, macOS, watchOS et tvOS. XCTest fait partie d'Xcode et prend en charge la rédaction de tests en Swift et Objective-C. Contrairement aux frameworks tiers (Quick, Nimble), XCTest est la solution officielle d'Apple et est entièrement intégré à Xcode Server et CI/CD. Selon Apple Developer (2024), XCTest est utilisé dans 94 % des applications iOS du top 100 de l'App Store. XCTest fournit une base stable pour écrire des tests unitaires et des tests d'interface utilisateur sans dépendances externes.
Points clés
XCTest est un framework pour les tests unitaires, d'intégration et d'interface utilisateur développé par Apple et intégré à Xcode depuis la version 5.0 (2013). XCTest a remplacé OCUnit (SenTestingKit) et a fourni une API moderne en Swift avec la prise en charge des tests asynchrones, des tests de performance et de l'intégration avec Xcode Server. Selon Swift.org (2024), XCTest est la base des tests dans tous les projets Apple, y compris Swift Package Manager, qui utilise XCTest pour l'auto-validation.
XCTest fonctionne avec Xcode Test Navigator et Report Navigator, qui affichent l'arborescence des tests, l'historique des exécutions et comparent les résultats entre les builds. Test Navigator permet d'exécuter un seul test, un groupe de tests ou l'ensemble de la suite sans modifier le code. Les résultats sont affichés avec des icônes vertes (réussite), rouges (échec) et jaunes (ignorés). Selon Apple WWDC (2024), Xcode 16 a amélioré l'exécution parallèle des tests de 40 % en utilisant plusieurs simulateurs.
XCTest prend en charge les plateformes : iOS 8.0+, macOS 10.10+, watchOS 2.0+, tvOS 9.0+. Chaque plateforme dispose de la même API, ce qui permet d'écrire des tests multiplateformes. Swift Testing — un nouveau framework d'Apple (annoncé en 2024) — complétera XCTest à l'avenir mais ne le remplacera pas entièrement. XCTest reste le framework de test principal dans l'écosystème Apple.
XCTestCase est la classe de base dont héritent toutes les classes de test dans XCTest. Elle fournit le cycle de vie du test : `setUp()` est appelé avant chaque test, `tearDown()` après chaque test. setUp est utilisé pour initialiser les objets et les mocks, tearDown pour nettoyer les ressources. setUpWithError et tearDownWithError permettent de gérer les erreurs d'initialisation sans try-catch dans chaque test.
Chaque méthode dont le nom commence par `test` est automatiquement reconnue par Xcode comme un test. Alternativement, la macro `@Test` (Swift Testing) peut être utilisée. Les noms de test doivent être descriptifs : `testLoginWithValidCredentials` est meilleur que `testLogin1`. Documenter les tests via des commentaires est une bonne pratique, mais Xcode permet également d'ajouter des descriptions via les User-Defined Attributes.
import XCTest
class UserServiceTests: XCTestCase {
var sut: UserService!
var mockSession: MockURLSession!
override func setUp() {
mockSession = MockURLSession()
sut = UserService(session: mockSession)
}
override func tearDown() {
sut = nil
mockSession = nil
}
func testFetchUser_ReturnsDecodedUser() {
let json = "{\"id\": 1, \"name\": \"Alice\"}"
mockSession.setResponse(json)
let user = try await sut.fetchUser(id: 1)
XCTAssertEqual(user.name, "Alice")
}
}
L'exemple ci-dessus montre la structure standard de XCTestCase. sut (System Under Test) est une convention de nommage pour l'objet testé. MockURLSession remplace le réseau réel, permettant de tester UserService de manière isolée. Le principe « un test — une assertion » simplifie le débogage : si un test échoue, le développeur sait immédiatement quelle fonctionnalité est cassée. Chaque test XCTestCase doit vérifier un scénario ou une assertion.
XCTAssertTrue et XCTAssertFalse sont des assertions de base pour vérifier les valeurs booléennes. XCTAssertTrue(expression) réussit si expression == true. XCTAssertEqual vérifie l'égalité de deux valeurs avec la prise en charge de tous les types implémentant Equatable. Pour les nombres à virgule flottante, XCTAssertEqual avec le paramètre accuracy est utilisé pour tenir compte de la précision du calcul. Selon Google Testing Blog (2024), XCTAssertEqual couvre 70 % de toutes les vérifications dans une suite de tests typique.
XCTAssertNil et XCTAssertNotNil vérifient les valeurs optionnelles pour nil. Ces assertions sont essentielles en Swift, où les types optionnels sont largement utilisés. XCTAssertThrowsError vérifie que le code lève une erreur attendue. XCTUnwrap est une assertion qui dépaquette une valeur optionnelle et échoue avec un message clair si la valeur est nil. La comparaison de chaînes via XCTAssertEqual utilise une comparaison littérale, pas sémantique. XCTAssertNoThrow est l'assertion associée pour vérifier que le code ne lève pas d'erreur.
| Assertion | Objectif | Exemple |
|---|---|---|
| XCTAssertEqual | Vérification d'égalité | XCTAssertEqual(a, b) |
| XCTAssertTrue | Vérification de vérité | XCTAssertTrue(result) |
| XCTAssertNil | Vérification de nil | XCTAssertNil(error) |
| XCTAssertThrowsError | Vérification d'erreur | XCTAssertThrowsError(try parse("")) |
| XCTUnwrap | Dépaquetage optional | XCTUnwrap(value) |
XCTestExpectation est un mécanisme pour tester le code asynchrone. Le test crée une attente avec un nom descriptif, la transmet à une opération asynchrone et appelle `wait(for:timeout:)`. Si l'attente n'est pas satisfaite dans le délai imparti, le test échoue. Délai d'attente par défaut est de 10 secondes, mais pour les opérations rapides, il est recommandé de définir 1 à 3 secondes pour accélérer le temps de test global.
XCTWaiter est une alternative plus flexible à wait(for:timeout:). XCTWaiter permet d'attendre plusieurs attentes, de configurer l'ordre d'exécution et de gérer les délais d'attente par programmation. Contrairement à wait, XCTWaiter retourne `XCTWaiter.Result`, qui peut être analysé. Délégué XCTWaiterDelegate notifie des violations de l'ordre des attentes et des délais d'attente.
func testAsyncLogin() {
let expectation = XCTestExpectation(description: "login")
var resultUser: User?
sut.login(email: "a@b.com", password: "123") { user in
resultUser = user
expectation.fulfill()
}
wait(for: [expectation], timeout: 3)
XCTAssertNotNil(resultUser)
XCTAssertEqual(resultUser?.email, "a@b.com")
}
Dans l'exemple, XCTestExpectation est utilisé pour tester une connexion asynchrone. fulfill() est appelé à l'intérieur de la closure du callback, signalant que l'opération asynchrone est terminée. Si fulfill() n'est pas appelé dans les 3 secondes, le test échoue avec un délai d'attente. Après une attente réussie, des assertions sont effectuées pour vérifier le résultat. Plusieurs attentes peuvent être passées sous forme de tableau et attendre que toutes soient satisfaites.
measure(metrics:) est une méthode XCTestCase pour créer des tests de performance. Le bloc de code à l'intérieur de measure s'exécute 10 fois de suite, et XCTest collecte des statistiques : temps moyen, médiane, écart type. Metrics est un tableau de métriques suivies : XCTClockMetric (temps), XCTMemoryMetric (mémoire), XCTStorageMetric (disque) et XCTCPUMetric (processeur). Selon Apple WWDC (2024), les tests de performance avec XCTCPUMetric sont utiles pour détecter les régressions dans les algorithmes.
Référence (ligne de base) pour les tests de performance est définie dans Xcode Test Plan. Si le temps d'exécution dépasse la référence d'un pourcentage défini (par défaut 10 %), le test est considéré comme échoué. La référence est mise à jour manuellement après confirmation que le changement de performance est attendu. Plan de test dans Xcode permet de regrouper les tests de performance par configuration : debug/release, différents appareils, différentes versions d'iOS.
func testArraySortPerformance() {
let numbers = (1...10000).shuffled()
measure(metrics: [XCTClockMetric()]) {
let _ = numbers.sorted()
}
}
Ce test de performance mesure le temps de tri d'un tableau de 10 000 éléments. XCTClockMetric capture le temps d'exécution réel. Si après avoir changé l'algorithme de tri, le temps augmente de 10 % ou plus, le test indiquera une régression. Les tests de performance XCTest sont particulièrement utiles pour : les algorithmes de traitement de données, le rendu des composants d'interface utilisateur, les opérations de base de données et les requêtes réseau.
La structure du projet de test dans XCTest suit la convention : un fichier de test par classe, placé dans un répertoire séparé `<TargetName>Tests`. Les noms de fichiers correspondent aux noms des classes testées avec le suffixe `Tests` : `UserService.swift` → `UserServiceTests.swift`. Les cibles de test dans Xcode sont configurées séparément pour les tests unitaires et les tests d'interface utilisateur, ce qui permet de les exécuter indépendamment. Schémas dans Xcode gèrent la configuration de build et l'ensemble des tests à exécuter.
Xcode Cloud et GitHub Actions prennent en charge l'exécution de XCTest via `xcodebuild test -scheme App -testPlan SmokeTest`. Le pipeline CI comprend : build → exécution des tests unitaires → exécution des tests d'interface utilisateur → publication du rapport. Le rapport JUnit est généré via `xcodebuild` avec l'option `-resultBundlePath` et peut être importé dans n'importe quel outil CI. Couverture de code est une fonction intégrée de XCTest qui montre quelles lignes de code sont couvertes par les tests. Le seuil minimum de couverture pour le code de production est de 70 % pour la logique métier critique.
Xcode Cloud et GitHub Actions prennent en charge l'exécution de XCTest via `xcodebuild test -scheme App -testPlan SmokeTest`. Le pipeline CI comprend : build → exécution des tests unitaires → exécution des tests d'interface utilisateur → publication du rapport. Rapport JUnit est généré via `xcodebuild` avec l'option `-resultBundlePath` et peut être importé dans n'importe quel outil CI. Bitrise et Jenkins ont des étapes prêtes pour XCTest.
Couverture de code est une fonction intégrée de XCTest qui montre quelles lignes de code sont couvertes par les tests. Xcode affiche la couverture en vert (couvert), rouge (non couvert) et jaune (partiellement couvert). Seuil minimum de couverture pour le code de production est de 70 % pour la logique métier critique. Selon Google Testing Blog (2024), imposer 80 % de couverture pour tous les modules conduit à des « tests vides » qui ne vérifient pas la logique mais exécutent seulement le code.
Questions fréquentes
XCTest est le framework officiel d'Apple avec une intégration directe dans Xcode. Quick et Nimble sont des bibliothèques tierces qui fournissent une syntaxe BDD et des assertions plus lisibles. Quick et Nimble sont pratiques pour les tests d'acceptation, mais XCTest est plus fiable pour les tests unitaires en raison de l'absence de dépendances externes.
Le code asynchrone est testé via XCTestExpectation + `wait(for:timeout:)` ou via les méthodes `async/await` de XCTest (iOS 13+). Pour les API basées sur des callbacks, une attente est créée et satisfaite dans la closure. Pour async/await, des assertions standard sont utilisées dans les fonctions async.
XCTest n'inclut pas de framework de simulation intégré. La simulation est implémentée via des protocoles : une classe mock est créée qui implémente le même protocole que la dépendance réelle. Pour la génération automatique de mocks, on utilise Cuckoo, SwiftyMocky ou des mocks manuels. L'injection de dépendances via les initialiseurs est une condition obligatoire pour la testabilité.
Oui, XCTest s'exécute sur des appareils réels via Xcode ou xcodebuild avec le paramètre `-destination 'platform=iOS,name=iPhone 15'`. Les tests d'interface utilisateur sur des appareils réels donnent des résultats plus précis que sur des simulateurs. Pour l'exécution sur des fermes d'appareils, on utilise BrowserStack, Sauce Labs ou Firebase Test Lab.
Swift Testing (2024) est un nouveau framework Apple avec les macros `@Test`, `@Suite` et `@Expect`. Il fournit une paramétrisation de test intégrée, un regroupement en suites et une syntaxe plus lisible. Swift Testing coexiste avec XCTest et ne le remplace pas. XCTest reste le framework principal pour les tests d'interface utilisateur et les tests de performance.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi