La cohérence des champs d’identité est la propriété qui rend un ensemble de valeurs générées utilisable tout court. Chaque champ d’un enregistrement peut être plausible individuellement tandis que l’enregistrement dans son ensemble décrit quelque chose d’impossible — une rue dans un pays, un code postal d’un autre, un numéro de téléphone appartenant à un troisième — et c’est précisément l’état dans lequel les tests cessent de dire la vérité.
Cet article explique comment naissent les enregistrements incohérents, pourquoi ils laissent des tests passer alors qu’ils devraient échouer, et ce qu’il faut pour qu’un jeu de données de test reste cohérent à mesure qu’il grandit.
Pourquoi des champs individuellement corrects forment-ils un enregistrement faux ?
Le hasard est le coupable habituel, et il est contre-intuitif de voir à quelle vitesse il produit des absurdités. Tirez un pays, puis tirez une ville indépendamment, et la paire sera en désaccord avec une probabilité proche de un. Les valeurs sont chacune tirées du bon vivier ; rien n’est malformé ; la combinaison ne se produit simplement jamais.
Les enregistrements construits à la main échouent différemment mais tout aussi souvent. Celui qui les écrit travaille généralement champ par champ, en vérifiant chaque valeur par rapport à ses propres connaissances, et n’a aucune raison de garder en tête en même temps le pays, la division, le code postal et l’indicatif téléphonique. Le résultat est un enregistrement qui paraît soigné et qui est intérieurement contradictoire.
Il existe une troisième source, plus discrète que les deux autres : les enregistrements assemblés à partir de plusieurs sources. Une ligne de préproduction peut tirer son nom d’une graine, son adresse d’une fixture, et ses coordonnées de ce que l’auteur du test avait ouvert. Chaque partie était correcte là d’où elle venait.
Où l’incohérence se transforme en faux positif
Considérez une règle selon laquelle un client doit avoir au moins un certain âge pour acheter un produit, et un test qui vérifie que la règle fonctionne. Avec une date de naissance dans un siècle et un âge stocké provenant d’un autre, le test peut exercer la branche d’acceptation alors que l’enregistrement testé aurait dû être rejeté — et rien ne signale de problème, car aucun contrôle n’a échoué.
| Paire incohérente | Ce qu’elle masque |
|---|---|
| Pays et forme du code postal | Toute la validation du code postal, car le code est encore bien formé |
| Pays et indicatif téléphonique | La logique de locale et de routage, qui bascule silencieusement sur une valeur par défaut |
| Date de naissance et seuil d’âge | Les barrières d’âge, si bien qu’un test réussi prouve seulement que le chemin d’acceptation s’exécute |
| Adresse et format d’identifiant | La validation de l’identifiant, qui est ignorée quand le pays est ambigu |
| Division et plage de codes postaux | La normalisation d’adresse, car aucune règle ne peut s’appliquer à une contradiction |
Le schéma est le même dans chaque ligne : l’incohérence supprime l’entrée qui aurait déclenché la défaillance. Une suite de tests remplie de tels enregistrements n’est pas faible parce qu’elle manque d’assertions ; elle est faible parce que ses entrées n’atteignent jamais les branches où vivent les assertions.
Des données incohérentes doivent-elles être rejetées ou réparées ?
Cela dépend de qui les détient, et se tromper là-dessus cause ses propres dégâts. Les données entrantes provenant d’une personne doivent être réparées lorsque l’intention est sans ambiguïté et interrogées lorsqu’elle ne l’est pas, car un être humain attend. Les données générées et les fixtures doivent être rejetées d’emblée, car il n’y a aucune intention à récupérer et un enregistrement réparé masque le défaut dans ce qui l’a produit.
La règle empirique pour un générateur est que l’incohérence est un bug du générateur plutôt qu’une propriété à tolérer en aval. Toute autre approche habitue l’équipe à écrire du code tolérant qui rencontrera plus tard une entrée réelle et l’absorbera.
Pour la validation dans le produit, la question utile est ce qu’un contrôle de cohérence échoué doit faire à l’enregistrement. Un code postal manquant est une lacune de saisie, et l’utilisateur peut la corriger. Un code postal qui existe mais appartient à une autre région est une contradiction, et aucun nombre de nouvelles tentatives de l’utilisateur ne la résoudra sans que la région change aussi. Ces deux cas méritent des traitements différents.
Quelle force une règle de cohérence doit-elle avoir ?
Rendez-la aussi faible que possible tout en interceptant les défaillances qui comptent, et soyez explicite sur ce qu’elle est. Les règles se déclinent en trois forces, et les mélanger sans étiquettes est la manière dont une base de code finit avec une validation que personne ne peut expliquer.
- Un avertissement enregistre le désaccord et laisse passer l’enregistrement, ce qui convient aux relations déduites plutôt qu’énoncées.
- Un rejet bloque l’enregistrement, ce qui convient lorsque le désaccord est impossible plutôt que simplement inhabituel.
- Une réparation réécrit une valeur pour l’accorder à une autre, ce qui est la plus dangereuse des trois et ne doit s’employer que lorsque la préséance est documentée.
L’essentiel de la confusion dans ce domaine vient du fait de traiter une corrélation statistique comme une règle stricte. Un indicatif téléphonique implique généralement un pays, mais pas toujours ; un code postal implique généralement une région, mais les zones frontalières et les exceptions existent. Encoder une tendance comme une interdiction rejette de vraies personnes, et elle le fait le plus souvent pour les utilisateurs qui ressemblent le moins aux hypothèses inscrites dans les données.
Les enregistrements générés doivent-ils être cohérents sur tout le lot ?
Au sein d’un enregistrement, oui. Sur tout un lot, non — et confondre les deux gaspille des efforts dans un sens et crée des défauts dans l’autre. Un lot de mille enregistrements devrait contenir de la variété : différentes régions, différentes formes de noms, différentes tranches d’âge, certains enregistrements avec des champs laissés absents. Il ne devrait pas contenir mille enregistrements qui prétendent chacun être la même personne.
Il y a une raison pratique de garder les lots intérieurement divers. Un test qui s’exécute contre une centaine d’enregistrements quasi identiques exerce un seul chemin de code cent fois et rapporte un succès. Le même test contre une centaine d’enregistrements variés exerce les branches qui comptent, ce qu’une répétition de charge ou de migration cherche réellement à établir.
Là où un lot a besoin d’homogénéité, c’est pour la reproductibilité : la même entrée doit produire le même lot, afin qu’une défaillance trouvée une fois puisse être retrouvée. Les enregistrements générés par le générateur d’identités et de données de test conservent cette propriété en dérivant tout d’une clé d’identité, ce qui permet à un lot d’être varié et reproductible à la fois. Les valeurs restent des enregistrements synthétiques pour les tests logiciels, et non les coordonnées de vraies personnes.
Quelles paires cassent le plus souvent en pratique ?
Quatre paires représentent la majorité des défauts. Le pays avec le bloc d’adresse est la première, car les formes d’adresse sont la partie la plus manifestement nationale d’un enregistrement. Le pays avec l’indicatif téléphonique est la deuxième, car les indicatifs sont courts et faciles à laisser non liés. La date de naissance avec la barrière d’âge est la troisième, car la barrière est généralement exprimée sous forme de nombre plutôt que de comparaison. Le pays avec le numéro d’identification est la quatrième, car un numéro qui a simplement une forme de chiffres passe un contrôle faible n’importe où.
Chacune de ces paires a un test peu coûteux : générez un enregistrement, modifiez à la main un membre de la paire, et confirmez que le système le remarque. S’il ne le remarque pas, la paire n’a jamais réellement été validée, et l’enregistrement qui aurait dû échouer passait depuis le début.
Pour les développeurs : où faire respecter les règles
Générez dans l’ordre des dépendances et validez dans le même ordre. Le pays d’abord, puis la division qui lui appartient, puis tout ce qui dérive de ces deux-là — adresse, code postal, indicatif téléphonique, format d’identifiant, devise et locale. Un générateur qui tire les champs dans l’ordre où le formulaire les affiche produira des contradictions, car l’ordre du formulaire relève de la présentation et l’ordre des données relève de la causalité.
Placez la validation inter-champs à un seul endroit plutôt que de l’éparpiller dans le formulaire, l’API et la base de données. Les règles dupliquées dérivent, et la version qui dérive est toujours celle que personne ne lit. Modélisez chaque paire explicitement pour qu’un relecteur voie quelle valeur contraint laquelle, et donnez à chaque règle une étiquette de force afin qu’un lecteur ultérieur sache si elle bloque ou avertit simplement.
Pour les fixtures, incluez un enregistrement délibérément incohérent à côté des valides — la même personne avec exactement une paire cassée — et affirmez que le système le rejette. Ce cas unique est la fixture la plus précieuse de l’ensemble, car c’est la seule qui prouve que les règles de cohérence sont encore activées. Là encore, tout cela est de la donnée synthétique pour les tests : ce n’est la description d’aucune personne réelle, et cela ne doit pas servir à en représenter une.
Étapes suivantes
Notez les cinq paires de champs sur lesquelles votre produit s’appuie et vérifiez chacune dans le code ; si une paire n’est appliquée nulle part, elle est supposée plutôt que vérifiée. Récupérez ensuite un lot d’enregistrements générés dans le générateur d’identités et faites-les passer par un chemin de migration ou d’import, en guettant les lignes qui réussissent pour la mauvaise raison. L’article sur les fixtures de test explique comment garder en permanence un enregistrement cassé dans la suite pour que cette propriété ne régresse jamais.