Après l’ajout d’un dégradé, du décodage d’images ou d’ombres complexes à une liste de produits, tous les tests fonctionnels peuvent rester au vert alors que le défilement commence à perdre des images. Quelques manipulations manuelles suffisent rarement à déterminer s’il s’agit d’une saccade ponctuelle ou d’une régression du code. Une méthode plus fiable consiste à figer le simulateur, le jeu de données et les gestes sur un Mac dans le cloud, à mesurer de façon répétée la durée, le CPU et la mémoire avec XCTMetric, puis à demander à l’intégration continue de ne bloquer que les écarts dépassant la référence.
Commencer par figer les variables qui influencent les résultats
La première étape d’un contrôle de performance n’est pas d’écrire des assertions, mais de réduire le bruit lié à l’environnement. Figez les versions de Xcode et de macOS, le modèle du simulateur, la langue du système, l’orientation de l’écran et les données de test. Le compte de test doit accéder directement à l’écran cible afin que les requêtes de connexion, le téléchargement des images et le temps de réponse du serveur ne faussent pas les mesures de défilement.
Ajoutez à l’application un argument de lancement réservé aux tests d’interface, par exemple -UITestSeed fixed. Il doit charger des fixtures locales, générer un nombre fixe d’éléments dans un ordre déterminé et désactiver les animations aléatoires. La liste doit également disposer d’identifiants d’accessibilité stables, comme feed.list et feed.reset. Le code de test ne doit pas rechercher les éléments à partir de textes localisés.
La première exécution comprend généralement l’installation de l’application, le chargement des bibliothèques dynamiques, l’initialisation des polices et le préchauffage des caches. Exécutez d’abord un test non comptabilisé avant les mesures officielles, mais ne supprimez pas les caches pour créer artificiellement un démarrage « entièrement à froid » qui ne correspond pas à l’usage quotidien.
Une référence de performance décrit un environnement complet, et non une durée universelle en millisecondes transposable à tous les Mac et à tous les simulateurs.
Définir un parcours de défilement reproductible
Un seul appel à swipeUp() dépend trop fortement du point de départ et de la longueur de la liste. Un test plus fiable doit d’abord revenir en haut de la liste, vérifier que le premier repère est visible, puis exécuter un nombre fixe de gestes. Réinitialisez à nouveau la position après chaque série de mesures afin que la suivante ne commence pas en bas de la liste.
import XCTest
final class FeedScrollPerformanceTests: XCTestCase {
func testFeedScrollPerformance() {
let app = XCUIApplication()
app.launchArguments += ["-UITestSeed", "fixed"]
app.launch()
let list = app.collectionViews["feed.list"]
XCTAssertTrue(list.waitForExistence(timeout: 10))
app.buttons["feed.reset"].tap()
XCTAssertTrue(app.cells["feed.item.0"].waitForExistence(timeout: 5))
let options = XCTMeasureOptions()
options.iterationCount = 5
measure(
metrics: [XCTClockMetric(), XCTCPUMetric(), XCTMemoryMetric()],
options: options
) {
app.buttons["feed.reset"].tap()
for _ in 0..<6 {
list.swipeUp(velocity: .fast)
}
}
}
}
Le bouton de réinitialisation peut n’apparaître qu’en mode test, mais il doit effectuer un défilement déterministe plutôt que redemander les données. Si l’écran utilise un chargement paginé, remplacez la couche réseau par des réponses locales ou mesurez explicitement le temps de pagination dans un test distinct.
Exécuter le test sur une destination fixe et archiver les résultats
Commencez par répertorier les simulateurs disponibles pour vérifier que l’intégration continue n’a pas changé silencieusement d’appareil. Attribuez ensuite au test un chemin unique pour le bundle de résultats. Tout ancien bundle doit être supprimé au préalable, faute de quoi xcodebuild refusera de l’écraser.
set -euo pipefail
RESULT="$PWD/artifacts/FeedScroll.xcresult"
rm -rf "$RESULT"
mkdir -p "$PWD/artifacts"
xcodebuild test \
-workspace App.xcworkspace \
-scheme AppUITests \
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
-only-testing:AppUITests/FeedScrollPerformanceTests \
-resultBundlePath "$RESULT"
OS=latest convient aux pipelines qui suivent systématiquement la chaîne d’outils courante. Pour effectuer des comparaisons à long terme, fixez une version précise de l’environnement d’exécution et reconstruisez la référence après toute mise à niveau de Xcode ou du système. Les tâches exécutées sur des nœuds OVPS doivent également consigner la configuration de l’hôte, l’identifiant du commit, la version de Xcode et l’environnement d’exécution du simulateur, afin de ne pas confondre une mise à niveau de l’environnement avec une dégradation du code.
Avant d’utiliser la version de xcresulttool fournie avec Xcode, consultez son aide, car les sous-commandes peuvent changer d’une chaîne d’outils à l’autre. Que les résultats soient traités par un script ou par un analyseur de rapports de test, le fichier xcresult brut doit être conservé comme pièce jointe en cas d’échec.
Évaluer les résultats avec la médiane et un seuil relatif
Ne bloquez pas une fusion à cause d’un seul échantillon lent. Exécutez cinq à sept séries pour chaque commit, écartez d’abord la série de préchauffage, puis calculez la médiane. La référence peut correspondre à la médiane des dernières exécutions stables de la branche principale, plutôt qu’à la toute première mesure conservée indéfiniment.
Il est recommandé de surveiller séparément trois catégories d’évolution. La durée totale reflète l’attente perçue par l’utilisateur, les mesures CPU mettent facilement en évidence les recalculs répétés de mise en page ou le rendu excessif, et les mesures de mémoire révèlent les problèmes de cache d’images et de réutilisation des vues. Si la durée augmente pour un commit alors que le CPU et la mémoire restent stables, commencez par vérifier la charge du simulateur. Une dégradation simultanée des trois mesures est davantage susceptible d’indiquer une régression du code de l’application.
Le seuil doit être proportionnel et inclure un écart absolu minimal. Par exemple, le test ne doit échouer que si la durée médiane dépasse la référence de plus de 12 % et si l’augmentation absolue excède un plancher de bruit défini par l’équipe. La valeur du seuil doit être déterminée d’après les fluctuations historiques observées dans le même environnement, et non copiée depuis un autre projet.
Conserver les preuves après un échec et circonscrire la cause
En cas d’échec du contrôle, ne remplacez pas immédiatement la référence. Relancez d’abord le même commit. Si l’échec persiste, conservez le fichier xcresult, les journaux complets, la version des données de test, les versions du système et de la chaîne d’outils, ainsi qu’un récapitulatif du CPU, de la mémoire et de la durée. Réduisez ensuite la plage de commits à examiner, en accordant une attention particulière au décodage des images, aux ombres et aux flous, aux contraintes de mise en page automatique, aux entrées-sorties synchrones sur le thread principal, à la réutilisation des éléments de liste et à la journalisation.
Il faut également distinguer une « dégradation des mesures » d’une « modification du parcours de test ». Si un identifiant d’accessibilité ne fonctionne plus ou si l’action de réinitialisation ne revient pas en haut de la liste, le test peut continuer à s’exécuter tout en mesurant une autre zone de l’écran. Ajouter des assertions de visibilité aux points de départ et d’arrivée essentiels est plus efficace que simplement multiplier les nouvelles tentatives.
Toute mise à jour de la référence doit faire l’objet d’un commit distinct, expliquer la mise à niveau de l’environnement ou l’ajustement d’interaction jugé acceptable, et inclure les échantillons relevés avant et après la modification. Le contrôle de performance évitera ainsi les déclenchements fréquents dus à un bruit ponctuel, sans perdre son utilité à cause de références actualisées sans justification.
Questions fréquentes
Faut-il imposer un seuil fixe en millisecondes ?
Non. Établissez une référence sur la même machine, avec le même système, le même simulateur et les mêmes données, puis appliquez un seuil relatif à la médiane récente.
Pourquoi exclure la première exécution de la référence ?
Elle peut inclure l’installation, le chargement des bibliothèques, l’initialisation des polices et le préchauffage des caches. Une exécution de chauffe réduit ce bruit.
Quels artefacts conserver après un échec ?
Conservez xcresult, les journaux, le commit, les versions de Xcode et de macOS, le modèle de simulateur, les données de test et les mesures CPU et mémoire.
Placez la prochaine compilation sur un nœud physique dédié
Choisissez parmi trois configurations Apple Silicon et six nœuds proposés à la vente. La disponibilité réelle est indiquée en temps réel dans la console.