Une grande partie de la programmation consiste à prendre des choses que vous avez utilisées maintes et maintes fois par
le passé et de les utiliser alors une nouvelle fois dans un contexte différent. Ces choses sont généralement certaines
structures de classes-données (comme des listes chaînées, des tables de hachage ou bases de données relationnelles) ou
des opérations (comme la recherche, le tri, la création de
fichiers temporaires ou l'incrustation d'une fenêtre de navigateur). Par exemple, deux bases de données relationnelles
client comporteront de nombreuses caractéristiques banales.
Ce qui est intéressant dans ces banalités, c'est qu'elles s'accompagnent d'erreurs banales. Les gens n'inventent pas de nouvelles manières imaginatives
d'insérer quelque chose d'erroné dans une liste doublement chaînée. Ils ont tendance à faire la même erreur que les
autres et eux-même ont fait auparavant. Un programmeur qui incruste une fenêtre de navigateur peut commettre l'une de
ces erreurs banales :
-
crée une nouvelle fenêtre alors qu'une déjà ouverte devrait être réutilisée
-
omet de rendre visible une fenêtre de navigateur masquée ou réduite
-
utilise Internet Explorer lorsque l'utilisateur a choisi un autre navigateur par défaut
-
omet de vérifier si le JavaScript est activé
Ces erreurs étant banales, les idées de test pour les déceler le sont également. Mettez ces idées de test
dans votre catalogue d'idées de test afin de pouvoir les réutiliser.
L'un des avantages d'un catalogue qu'une seule idée de test peut être utile pour déceler plusieurs fautes
sous-jacentes. Voici un exemple d'idée qui trouve deux fautes.
La première faute était dans un compilateur C. Ce compilateur prenait des options de ligne de commande telles que
"-table", "-trace" ou "-nolink". Les options auraient pu être abrégées à leur forme unique la plus petite. Par exemple,
"-ta" fonctionnait aussi bien que "-table". Toutefois, "-t" n'était pas autorisé en raison de son ambiguïté : cette
option pouvait signifier "-table" ou "-trace".
En interne, les options de ligne de commande étaient stockées dans une table comme celle-ci :
Quand option était trouvée dans la ligne de commande, elle était recherchée dans la table. S'il elle correspondait au
préfixe d'une entrée de la table, alors elle correspondait à cette entrée. Par exemple, "-t" correspondait à "-table".
Une fois une correspondance trouvée, la recherche continuait dans le reste de la table pour en trouver une autre. Une
autre correspondance constituerait une erreur car elle indiquerait une ambiguïté.
Le code de recherche ressemblait à cela :
for (first=0; first < size; first++)
{
if (matches(entry[first], thing_sought))
{ /* at least one match */
for(dup=first+1; dup < size; dup++) /* search for another */
if (matches(entry[dup], thing_sought)) /* extra match */
break; /* error out */
return first;
}
}
return -1; /* Not found or ambiguity */
Comprenez-vous où se trouve le problème ? Il est assez subtil.
Le problème provient de l'instruction d'interruption. Elle vise à interrompre la boucle enveloppante extérieure
lorsqu'une correspondance double est trouvée mais elle interrompt en réalité la boucle intérieure. Cela a le même effet
que si aucune seconde correspondance n'était trouvée : l'index de la première correspondance est renvoyé.
Notez que cette erreur ne peut être décelée que si l'option recherchée a deux correspondances dans la table, comme cela
pourrait être le cas pour "-t".
Examinons maintenant une seconde erreur, complètement différente.
Le code prend une chaîne. Il est censé remplacer le dernier signe '=' dans la chaîne par un signe '+'. S'il n'y a aucun
signe '=', rien ne se produit. Ce code utilise la routine de bibliothèque C standard suivante : strchr. En voici la teneur :
ptr = strchr(string, '='); /* Find last = */ if (ptr != NULL_CHAR) *ptr = '+';
Le problème est ici aussi assez subtil.
La fonction strchr renvoie la première correspondance dans la chaîne, pas la dernière. La
fonction correcte est strrchr. Le problème provenait très certainement d'une faute
de frappe. (En fait, le profond problème sous-jacent est qu'il n'est absolument pas raisonnable de mettre deux
fonctions qui varient uniquement d'un caractère dans une bibliothèque standard.)
Cette erreur ne peut être décelée que lorsqu'il y a au moins deux signes égal dans l'entrée. En d'autres termes :
-
"a=b" renverrait le bon résultat : "a+b".
-
"noequals" renverrait le bon résultat : "noequals".
-
"a=b=c" renverrait le mauvais résultat : "a+b=c", et non le bon : "a=b+c"
Cet exemple est intéressant et utile : il montre que deux erreurs provenant de causes complètement différentes (faute
de frappe, mauvaise compréhension d'une construction C) et manifestées dans le code de façon différente (mauvaise
fonction appelée, mauvaise utilisation d'une instruction de rupture) peuvent être décelées par la même idée de
test (recherche d'un élément qui se produit deux fois).
En quoi un catalogue est-il bon ?
-
Il contient un petit ensemble d'idées de test qui peuvent trouver un ensemble d'erreurs sous-jacentes beaucoup plus
vaste.
-
Il est facile et rapide à lire (parcourir). Vous devez être capable de passer les idées de test qui ne s'appliquent
pas dans votre situation.
-
Il ne contient que les idées de test que vous utiliserez. Par exemple, une personne qui ne travaille jamais avec
les navigateurs Web ne devrait pas avoir à passer à chaque fois les idées de test pour les programmes qui utilisent
les navigateurs Web. Une personne qui travaille sur des logiciels de jeu souhaitera un catalogue plus court que
celle qui travaille sur un logiciel dans lequel la sécurité est critique. La personne qui travaille sur les jeux
peut se permettre de se concentrer uniquement sur les idées de test avec la plus grande probabilité de trouver des
erreurs.
Etant donné ces règles, il semble préférable d'avoir plusieurs catalogues. Lorsque certaines données et opérations sont
communes à toute la programmation, leurs idées de test peuvent être consignées dans un catalogue que tous les
programmeurs peuvent utiliser. Lorsqu'elles sont spécifiques à des domaines particuliers, leurs idées de test peuvent
être consignées dans un catalogue spécifique à un domaine.
Le catalogue échantillon (Télécharger Adobe Reader), utilisé dans la section
suivante, constitue un bon point de départ. Vous pouvez également utiliser les Idées de test pour des combinaisons de AND et de OR.
Voici comment vous pouvez utiliser le catalogue échantillon. Supposons que vous implémentiez cette méthode :
void applyToCommonFiles(Directory d1, Directory d2, Operation op);
applyToCommonFiles prend deux répertoires comme arguments. Lorsqu'un fichier du premier
répertoire a le même nom qu'un fichier du second, applyToCommonFiles effectue une opération sur
cette paire de fichiers. Elle traite les sous-répertoires.
Pour utiliser le catalogue, il suffit de le parcourir et de rechercher les grands titres qui correspondent à votre
situation. Examinez les idées de test sous chaque titre pour voir si elles sont pertinentes puis inscrivez celles qui
sont pertinentes dans une Liste d'idées de
test.
Remarque : cette description étape par étape peut rendre laborieuse l'utilisation du catalogue. Il faut plus de
temps pour lire les instructions de création d'une liste de contrôle que pour la créer.
Ainsi, dans le cas de la méthode applyToCommonFiles, vous pouvez appliquer le catalogue selon la
description qui suit.
La première entrée est pour N'importe quel objet. Des arguments peuvent-ils être des pointeurs nuls ? C'est une
question de contrat entre la méthode applyToCommonFiles et ses appelants. Le contrat peut
établir que les appelants ne passeront pas dans un pointeur nul. S'ils le font, vous ne pouvez pas compter sur le
comportement prévu : applyToCommonFiles pourrait effectuer n'importe quelle action. Dans ce cas,
aucun test n'est approprié car aucune action de la méthode applyToCommonFiles ne peut être
incorrecte. Cependant, si applyToCommonFiles était requis pour vérifier les pointeurs nuls,
l'idée de test serait utile. Si l'on part de ce principe, cela nous donne la liste d'idées de test de départ :
-
d1 est nul (cas d'erreur)
-
d2 est nul (cas d'erreur)
-
op est nul (cas d'erreur)
L'entrée du catalogue suivante est Chaînes. Les noms des fichiers sont des chaînes et sont comparés pour voir
s'ils correspondent. L'idée d'effectuer un test avec la chaîne vide ("") ne paraît pas utile. Il se peut que des
routines standard de comparaison de chaînes soient utilisées, et qu'elles traitent correctement les chaînes vides.
A propos... Si des chaînes sont comparées, qu'en est-il de la casse ? Supposons que d1 contienne
un fichier nommé "File". d2 contient également un fichier nommé "file". Ces fichiers doivent-ils
correspondre ? Sur UNIX, il est clair que non. Sur Microsoft Windows, il est pratiquement certain que oui. C'est une
autre idée de test :
-
Les fichiers correspondent dans les deux répertoires, mais la casse des noms est différente.
Remarquez que cette idée de test n'est pas venue directement du catalogue. Toutefois, le catalogue a attiré notre
attention sur un aspect particulier du programme (noms de fichier comme chaînes) et notre créativité nous a donné
l'idée en plus. Il est important de ne pas utiliser le catalogue de trop près, utilisez-le comme une source de
réflexion et d'inspiration pour de nouvelles idées.
L'entrée suivante est Collections. Un répertoire est une collection de fichiers. De nombreux programmes qui
gèrent les collections échouent lorsqu'une collection est vide. Un petit nombre de programmes qui gère les collections
vides, ou les collections contenant de nombreux éléments, échoue lorsqu'il n'y a qu'un seul élément. Ces idées sont
donc utiles :
-
d1 est vide
-
d2 est vide
-
d1 a exactement un fichier
-
d2 a exactement un fichier
L'idée suivante est d'utiliser une collection de la taille maximum possible. applyToCommonFiles
serait normalement utilisée sur de petits répertoires. Ensuite, un utilisateur applique à ces répertoires deux énormes
arborescences contenant des milliers de fichiers et découvre alors que le programme a une mémoire ridicule et qu'il ne
peut pas gérer ce cas réaliste.
Ceci posé, il n'est pas important de tester la taille maximum absolue d'un répertoire ; il suffit qu'il s'agisse d'un
répertoire aussi gros que celui que pourrait avoir un utilisateur. Cependant, il faut au moins un test avec plus
de trois fichiers dans un répertoire :
-
d1 contient un très grand nombre de fichiers
-
d2 contient un très grand nombre de fichiers
L'idée de test finale (éléments doubles) ne s'applique pas aux répertoires de fichiers. Si dans un répertoire, deux
fichiers ont le même nom, le problème ne vient pas de la méthode applyToCommonFiles: c'est votre
système de fichiers qui est corrompu.
L'entrée du catalogue suivante est Recherche. Ces idées peuvent être traduites comme suit, en termes de méthode
applyToCommonFiles :
-
d1 et d2 n'ont aucun fichier en commun (tous les noms sont différents)
-
d1 et d2 ont exactement un fichier en commun (c'est le dernier élément du répertoire dans l'ordre alphabétique)
-
d1 et d2 ont plus d'un fichier en commun
L'idée de test finale vérifie la méthode applyToCommonFiles. Renvoie-t-elle la première
correspondance dès qu'elle en trouve une ? La remarque entre parenthèses dans l'idée de test ci-dessus part du principe
que le programme trouvera la liste de fichiers dans un répertoire à l'aide d'une routine de bibliothèque qui les
renvoie, triés par ordre alphabétique. Sinon, il peut être préférable d'utiliser le dernier élément comme
correspondance. Avant de consacrer beaucoup de temps à comprendre la manière dont les fichiers sont ordonnés,
demandez-vous quel serait le taux de probabilité de détection d'incidents si vous placiez en dernier l'élément
correspondant. Mettre un élément en dernier dans une collection est plus utile si le code explore explicitement étape
par étape la collection à l'aide d'un index. S'il utilise un itérateur, il est très improbable que l'ordre importe.
Examinons une autre entrée du catalogue échantillon. L'entrée structures liées nous rappelle que nous comparons
des arborescences de répertoires, pas seulement de simples collections de fichiers. Le choix d'un mode de test
pour la méthode applyToCommonFiles nous oblige à faire face au caractère inachevé de sa
description.
Si la structure du répertoire est la suivante :
Figure 1 : une structure de répertoire
La méthode applyToCommonFiles traite-t-elle le répertoire Cdir? Cela ne
semble pas logique. Il ne peut y avoir aucune correspondance avec aucun élément situé dans l'autre arborescence de
répertoires. En fait, il semble que les fichiers des sous-répertoires ne peuvent correspondre que si les noms des
sous-répertoires correspondent. C'est-à-dire, supposons que nous ayons cette structure de répertoire :
Figure 2 : une seconde structure de répertoire
Les fichiers nommés "File" ne correspondent pas entre eux car ils se trouvent dans des sous-répertoires différents. Les
sous-répertoires devraient être traités uniquement s'ils ont le même nom dans les deux emplacements : d1 d2. Cela amène à ces idées de test :
-
un sous-répertoire dans d1 est introuvable dans d2 (non traité)
-
un sous-répertoire dans d2 est introuvable dans d1 (non traité)
-
un sous-répertoire apparaît dans d1 et dans d2 (traité)
Mais cela soulève d'autres questions. L'opération (op) doit-elle être appliquée aux
sous-répertoires correspondants ou seulement aux fichiers correspondants ? Si elle est appliquée aux sous-répertoires,
doit-elle l'être avant de descendre l'arborescence ou après ? La différence entre les deux est importante si, par
exemple, l'opération efface le fichier ou répertoire correspondant. Pour cela, est-ce que l'opération doit être
autorisée à modifier la structure du répertoire ? Et plus précisément : quel est le comportement correct de la méthode
applyToCommonFiles si tel est le cas ? (Le même problème se présente avec les itérateurs.)
Les questions de ce genre se posent généralement lorsque vous lisez attentivement la description d'une méthode
concernant la création d'idées de test. Mais laissons-les de côté pour le moment. Quelles que soient les réponses, il
leur faudra des idées de test qui vérifient que le code implémente correctement les réponses.
Revenons au catalogue. Nous n'avons toujours pas considéré toutes ses idées de test. La première - vide (rien dans la
structure)- demande une répertoire vide. Nous avons déjà eu cela avec l'entrée Collections. Nous avons également
eu la structure minimale non-vide, qui est un répertoire avec un seul élément. Ce genre de redondance n'est pas
rare, mais il est facile de passer à côté.
Et une structure circulaire ? Les structures de répertoire ne peuvent pas être circulaires, un répertoire ne
peut pas être un de ses descendants ou dans lui-même... si ? Et les raccourcis (sous Windows) ou les liens symboliques
(sous UNIX) ? S'il existe un raccourci dans l'arborescence de d1qui pointe vers d1, la méthode applyToCommonFiles devrait-elle continuer à descendre
l'arborescence indéfiniment ? La réponse pourrait mener à une ou plusieurs nouvelles idées de test :
-
d1 est circulaire en raison de raccourcis ou liens symboliques
-
d2 est circulaire en raison de raccourcis ou liens symboliques
En fonction du comportement approprié, il peut y avoir plus d'idées de test que cela.
Enfin, qu'en est-il de la profondeur supérieure à un ? Les idées de test précédentes garantiront que nous
testons la descente de l'arborescence jusqu'à un niveau de sous-répertoire, mais nous devons vérifier que la méthode
applyToCommonFiles continue de descendre l'arborescence :
-
sur plusieurs niveaux (>1) des sous-répertoires de d1
-
sur plusieurs niveaux (>1) des sous-répertoires de d2
Comme indiqué plus haut, le catalogue générique ne contiendra pas toutes les idées de test dont vous avez besoin. Mais
des catalogues spécifiques au domaine n'ont pas été rendus publics par les entreprises qui les ont créés. Si vous
les voulez, vous devrez les faire vous-même. Voici quelques conseils.
-
Ne remplissez pas un catalogue d'hypothèses potentiellement utiles à la découverte d'erreurs. Souvenez-vous que
chaque idée de test que vous consignez dans le catalogue vous coûte du temps et de l'argent :
-
votre propre temps pour maintenir le catalogue
-
le temps d'autres programmeurs pour réfléchir à l'idée de test
-
et éventuellement le temps d'autres programmeurs pour implémenter un test
N'ajoutez que les idées qui ont fait leurs preuves. Vous devriez pouvoir au moins signaler une erreur réelle que
l'idée de test a décelée. Dans l'idéal, l'erreur doit avoir été omise par d'autres tests ; elle doit avoir été
signalée sur le terrain. Une bonne façon de créer des catalogues est de parcourir la base de données de bogues de
l'entreprise et de s'interroger sur la façon dont chaque erreur aurait pu être détectée plus tôt.
-
-
Le catalogue d'idées de test a peu de chances de fonctionner si sa création et sa maintenance relèvent du
passe-temps pour vous. Vous aurez besoin de temps spécialement consacré à cette tâche, comme pour toute autre tâche
d'importance. Nous vous recommandons de créer et de maintenir votre catalogue d'idées de test au cours de l'Activité : Amélioration des actifs de test.
|