De nombreux paramètres et valeurs rendent coûteux les tests combinés exhaustifs. Les tests par paires réduisent le nombre de tests tout en couvrant chaque paire de valeurs de paramètres. Il équilibre souvent le coût des tests avec la probabilité de détecter des défauts.
Pourquoi les tests par paires sont utiles
De nombreux défauts résultent d’interactions entre deux facteurs plutôt que de rares combinaisons de cinq ou six. Couvrir chaque paire permet donc de détecter une part substantielle de problèmes avec beaucoup moins de tests.
Où l'appliquer
- →Formulaires avec plusieurs champs et valeurs indépendants.
- →Combinaisons d'appareils, de navigateurs, de rôles, de forfaits, de pays et de langues.
- →API avec de nombreux paramètres et modes d'appel.
- →Matrices de compatibilité où les tests exhaustifs sont trop coûteux.
Ce qu'il faut retenir
- →Les tests par paires ne remplacent ni les vérifications de risques métier critiques ni les combinaisons dangereuses connues.
- →Les paramètres dépendants ne doivent pas être mélangés aveuglément avec des paramètres indépendants.
- →Excluez à l’avance les combinaisons logiquement impossibles.
- →Ajoutez des scénarios critiques à côté de l’ensemble généré par paires.
Conseils pratiques
Utilisez les tests par paires pour optimiser la couverture, pas pour justifier une suite minimale à tout prix. Identifiez manuellement les cas à haut risque, complétez-les par une couverture par paires, puis renforcez l’ensemble avec des scénarios négatifs et des valeurs limites.
Pairwise aide lorsque les combinaisons de paramètres constituent le véritable problème. Ce n'est pas une solution universelle pour les séquences, états ou calculs complexes.
Exemple concret : quatre tests couvrant les douze paires
Ce modèle fictif comprend trois facteurs : navigateur (Chrome ou Firefox), langue de l’interface (ru ou en) et rôle du compte (viewer ou editor). Les huit combinaisons sont autorisées. Les versions de navigateur, l’OS, le jeu de données et les fonctionnalités activées restent fixes pendant l’exercice ; relevez leurs valeurs exactes lors d’un vrai test navigateur. Ce modèle sélectionne les entrées ; ce n’est pas une application web exécutable.
La couverture de toutes les combinaisons exige 2 × 2 × 2 = 8 lignes. La couverture par paires vise chaque paire de valeurs de facteurs différents : quatre paires navigateur-langue, quatre navigateur-rôle et quatre langue-rôle. Les quatre lignes suivantes couvrent ces douze paires.
ID Navigateur Langue Rôle
PW1 Chrome ru viewer
PW2 Chrome en editor
PW3 Firefox ru editor
PW4 Firefox en viewerAuditez la couverture plutôt que de faire confiance au nombre de lignes
Facteur A Facteur B Couvert par
Navigateur=Chrome Langue=ru PW1
Navigateur=Chrome Langue=en PW2
Navigateur=Firefox Langue=ru PW3
Navigateur=Firefox Langue=en PW4
Navigateur=Chrome Rôle=viewer PW1
Navigateur=Chrome Rôle=editor PW2
Navigateur=Firefox Rôle=viewer PW4
Navigateur=Firefox Rôle=editor PW3
Langue=ru Rôle=viewer PW1
Langue=ru Rôle=editor PW3
Langue=en Rôle=viewer PW4
Langue=en Rôle=editor PW2Chaque ligne du tableau nomme une paire requise et le test la contenant. Le tableau couvre chaque paire une fois. Quatre tests sont minimes pour ce modèle binaire sans contrainte : les quatre paires navigateur-paramètres régionaux nécessitent à elles seules au moins quatre lignes. Cette conclusion s’applique uniquement à ce modèle spécifique, et non aux suites arbitraires par paires.
Vérifiez la matrice avec Python 3
Copiez ce code dans pairwise_coverage.py et exécutez python3 pairwise_coverage.py. Il utilise uniquement la bibliothèque standard et n’envoie aucune requête réseau. Sortie attendue : Pairs: 12/12; triples: 4/8.
from itertools import product, combinations
levels = [("Chrome", "Firefox"), ("ru", "en"), ("viewer", "editor")]
rows = [
("Chrome", "ru", "viewer"),
("Chrome", "en", "editor"),
("Firefox", "ru", "editor"),
("Firefox", "en", "viewer"),
]
required = {(i, a, j, b) for i, j in combinations(range(3), 2)
for a, b in product(levels[i], levels[j])}
covered = {(i, row[i], j, row[j]) for row in rows
for i, j in combinations(range(3), 2)}
assert all(row in set(product(*levels)) for row in rows)
assert covered == required
assert ("Chrome", "en", "viewer") not in rows
print(f"Pairs: {len(covered)}/{len(required)}; triples: {len(set(rows))}/8")Donnez à chaque configuration un oracle comportemental
Pour une fonction de document fictive, définissez la règle avant l’exécution : un viewer peut lire, mais une tentative de modification laisse le contenu enregistré inchangé ; un editor peut enregistrer une modification valide une seule fois. Pour chaque ligne, vérifiez la langue choisie, le résultat propre au rôle et les données enregistrées. La couverture des paires ne prouve pas ces résultats : une configuration déclarée réussie n’apporte aucune preuve sans assertions.
Un défaut déclenché uniquement par Chrome + en + viewer est manqué par cette matrice : ce triplet est absent même si toutes ses paires apparaissent ailleurs. Ajoutez cette combinaison lorsque le risque ou un défaut antérieur le justifie. Les contrôles de séquence, de limites, d'autorisation et de performances nécessitent toujours une conception distincte. Aucun pourcentage de défauts trouvés ne découle des « paires 12/12 ».
Si une contrainte rend une combinaison impossible, générez d’abord les combinaisons complètes autorisées, déduisez-en les paires réalisables, puis régénérez et vérifiez la suite réduite. Supprimer simplement une ligne invalide peut faire perdre des paires valides. Exercice : retirez PW4 ; Firefox–en, Firefox–viewer et en–viewer ne sont plus couverts. Restaurez une ligne de couverture valide plutôt que d’abaisser silencieusement le dénominateur.
Pour aller plus loin
Choisissez des cas supplémentaires par risque