• Aucun résultat trouvé

Principe et principales problématiques du test de logiciel

2.3 Test de logiciel

2.3.1 Principe et principales problématiques du test de logiciel

Le but du test de logiciel est de détecter les erreurs des programmes. Il existe une confusion entre les termes faute, erreur, et défaillance. Nous retenons la définition de Laprie [Laprie'95] qui se comprend en deux phrases :

« Le programmeur effectue une faute en introduisant une erreur dans le programme. Cette erreur entraîne une défaillance du fonctionnement du programme. »

Cependant Xanthakis inverse l’emploi des termes faute (défaut) et erreur [Xanthakis'92]. Nous ne porterons pas attention à cette confusion car nous nous intéressons uniquement à vérifier que l’implantation d’un programme est correcte.

Les tests d’un logiciel sont liés aux techniques de développement utilisées. Ainsi, il est nécessaire de faire évoluer conjointement les techniques de développement et les techniques de test qui leur sont associées. Par exemple, dans un cycle classique en V, quatre phases de test correspondent à quatre phases de développement (figure 2-10). Les tests unitaires vérifient l’implantation du programme par unité. La taille d’une unité dépend du paradigme du langage de programmation : une classe dans les programmes orientés objet ou une procédure. Les tests d’intégration vérifient les interactions entre plusieurs unités d’un assemblage. Les tests systèmes vérifient la totalité du système en intégrant tous les groupes d’unités utilisés lors du test d’intégration. Les tests de recette valident le système par rapport à la spécification initiale et aux attentes du client. Chaque paradigme de programmation a ses propres spécificités pour le test. Par exemple, l’introduction des classes dans la programmation par objet nécessite de vérifier les interactions entre classes dans les tests d’intégration. Dans cette thèse, nous étudions les spécificités du test de transformations de modèles.

Certaines conditions influencent la testabilité d’un programme. Voas [Voas'92a] identifie les trois principales : une partie du programme doit pouvoir être exécuté, si cette partie est erroné il faut que son exécution modifie l’état normal des données, et ce changement d’état doit être observable. Les tests d’une transformation de modèles doivent solliciter ses erreurs pour que les modèles de sortie soient différents de ce qui est spécifié.

Test de recette Implémentation Test système Analyse Conception architecturale Conception détaillée Test d’intégration Test unitaire Spécification

Figure 2-10 - Cycle de développement en V

L’exécution des tests est statique ou dynamique. Un test est statique s’il est réalisé sans exécuter le programme testé. Le programme est vérifié manuellement ou par des techniques

d’analyse statique de code. Un test est dynamique quand le programme est exécuté pour provoquer sa défaillance et révéler la présence d’une erreur. Dans cette thèse, nous étudions le test dynamique.

a- Processus du test dynamique de logiciel

D’un point de vue synthétique, le processus du test dynamique de logiciel consiste à exécuter un programme avec un ensemble de données en entrée et à vérifier que les résultats obtenus sont ceux attendus. Si ce n’est pas le cas, ce test a détecté une erreur qu’il faut localiser et corriger. Il faut systématiquement réappliquer les tests après chaque correction. Généralement, il existe une infinité de données d’entrée, donc il faut définir et appliquer un critère de test pour décider quand le programme a été suffisamment testé.

Dans la figure 2-11, nous illustrons ce processus en employant la même terminologie [Xanthakis'00] que dans le reste de la thèse. Le programme exécuté par les tests est le programme sous test. Les données d’entrée d’un programme sous test sont des données de test. Un oracle vérifie l’exactitude d’une donnée produite par l’exécution d’une donnée de test. L’oracle produit le verdict du test : le test passe ou échoue. Il y a régression quand corriger une erreur introduit une autre erreur. Finalement, un cas de test regroupe : une donnée de test, l’oracle correspondant, d’autres informations comme l’état du système avant l’exécution du test [Binder'99].

Les principales problématiques du test dynamique de logiciel portent sur ces activités (figure 2-11). Les techniques de test pour mettre en œuvre ces activités sont de deux types : fonctionnel ou structurel [Beizer'90] :

ƒ Le test fonctionnel n’exploite pas la structure du programme qui est considéré dans une « boîte noire ». De cette manière, les activités du test ne sont pas affectées par les changements effectués dans le code du programme.

ƒ Le test structurel exploite la structure du programme qui est considéré dans une « boîte blanche ».

Nous abordons la création de données de test, l’élaboration de critères d’arrêt, la localisation des erreurs dans les points suivants. Nous consacrons les sous-sections suivantes à deux problématiques traitées dans le reste du document : l’analyse de mutation (participant aux activités de critère d’arrêt et de création de données de test) et l’oracle.

b- Critères de test

Le test ne peut pas être exhaustif car les données de test appartiennent généralement à des domaines d’entrée non bornés ou trop grands. La valeur du test repose sur la sélection d’un ensemble de données de test suffisant. Pour cela les données de test doivent satisfaire un critère de test (ou critère d’arrêt). Le critère de test s’assure que les cas de tests ont un pouvoir de détection d’erreur suffisant. L’élaboration de critères de test s’appuie sur la structure du programme (si le test est structurel) ou uniquement sur la spécification (si le test est fonctionnel).

31 Test de logiciel Légende : Création de données de test Programme sous test Données de test Donnée de sortie Exécution des tests

Oracle Localisation/Correction de l’erreur Critère de test test échoue test passe verdict

non oui activité décision

Donnée assez de

tests

Figure 2-11 - Processus de test dynamique de logiciel

La plupart des travaux élaborent des critères de test structurel qui s’appuient sur la couverture : du code, du graphe de flot de contrôle, ou encore du graphe de flot de données. Par exemple, chaque instruction doit être exécutée au moins une fois, ou chaque chemin du graphe de flot de contrôle/données doit être parcouru un certain nombre de fois [Harrold'94].

Les critères de test fonctionnels exploitent uniquement la spécification. Dans ce cas, des critères déterminent la couverture du domaine d’entrée du programme sous test [Ostrand'88]. Si le comportement du programme est spécifié par des machines à états, il existe des critères de test qui déterminent leurs couvertures [McGregor'01]. Les machines à états sont similaires à des graphes, les exploiter pour mettre au point des critères de test est similaire à l’exploitation des graphes de flot de contrôle/données (couverture de nœuds, d’arcs, de chemins).

D’autres approches existent, comme l’analyse de mutation dont nous introduisons l’approche et les travaux dans la sous-section suivante et que nous étudions en détail dans le chapitre 3.

c- Localisation d’erreurs

La localisation d’erreurs (ou diagnostic) consiste à trouver la position des erreurs dans l’implantation. C’est une activité coûteuse car elle est majoritairement réalisée à la main. Dans ce cas, le but est d’assister la localisation. Les principaux travaux proposent d’exploiter la trace d’exécution du programme. Elle est fournie par des outils de débogage et informe sur l’évolution de l’état du programme pendant son exécution.

Les techniques d’assistance au diagnostic déterminent un ensemble d’instructions suspectes du programme sous test. Pour cela, elles réalisent plusieurs exécutions du programme. Dans [Agrawal'95], Agrawal et al. proposent une technique qui exploite deux exécutions : une

correcte et une défaillante. Les instructions qui sont parcourues uniquement par l’exécution défaillante sont identifiées comme suspectes. D’autres travaux ont amélioré cette technique [Jones'02a, Eagan'01] par l’exploitation de toutes les exécutions disponibles et le classement des instructions dans un ordre de suspicion. Des travaux de Baudry et al. [Baudry'06b] contribuent également à l’amélioration de ces techniques. Ils considèrent la création d’un minimum de cas de test permettant d’obtenir suffisamment d’exécutions tout en réduisant le coût global du test.

Les travaux de « reverse engineering » permettent d’obtenir un modèle structurel du programme à partir de son implantation. Dans ce cas, l’appréhension du programme au niveau modèle facilite la correction des erreurs. Les travaux de Delamare et al. [Delamare'06] participent en plus à la localisation des erreurs en fournissant un modèle comportementale du programme à partir de traces d’exécution.