---
title: "PdP: les tests"
date: 2024-02-14
tags: ["tests", "spécification", "validation"]
categories: ["Projet de programmation", "Cours"]
---

Les tests logiciels sont partiels, non exhaustifs et souvent approximé. Il se
font tout au long du cycle de developpement mais commencent avant les
implémentations. Il sont là pour assurer ou réfuter les propriétés du logiciels
et renforcer / compléter *une spécification*.

Une **spécification** est une description abstraite et rigoureuse de 
fonctionnalités, comportement et services "rendus".

La *validation d'une spécification* permet de vérifier l'adéquation de celle-ci
à ce qui a été dévellopé.

La *vérification d'une spécification* permet de s'assurer que l'implémentation
est correcte et qu'elle fonctionne en rapport avec notre spécification.

## Validation

La validation consiste donc à **analyser** si notre spécification reflète bien
ce qui est requis (définis dans le cahier de besoin par exemple). D'après
Sommerville (2005) ça passe par:

 * Le contrôle de **l'adéquation** avec les parties prenantes;
 * Le contrôle de **la complétude**, de tout de qui a été réalisé;
 * Le contrôle de **la consistance**, qu'il n'y a pas de contradiction;
 * le contrôle de **la faisabilité** de ce qui est à réaliser;
 * le contrôle de **la vérifiabilité**, que ce qui va être réaliser peut être
   vérifier.

Cette vérification est le plus souvent informelle et **non automatisable**
(difficilement formalisable, prouvable, quantifiable).

Cependant plusieurs techniques permettent de valider:

 * La **revue des besoins**;
 * Le **prototypage**;
 * La construction de **tests**.

## Vérification

Consitste à analyser que l'implementation est bien le reflet de la
spécification. Elle prend deux formes:

 * La vérification **formelle** qui implique un contrôle mathematique des
   propriété par des méthodes formelles et *mathématiques*.
 * La vérification **pratique** par des test de propriété de notre
   implementation au regard de sa spécification.

### Vérification formelle

elle utilise des langages abstrait de spécification, des outils d'aide à la
spécification et à son contrôle, des langages de programmation incluant des
éléments de spécification (comme *Agda*, *Idriss*), des outils d'aide à la
preuve comme *Coq*, *Isabelle* ou *Lean*.

Cependant, **toute propriété non triviale d'un programme est indécidable**.
Ce qui signifie que la vérification formelle est souvent *difficile*,
*fastidieuse* même si elle est vecteur de *progrès*.

Elle est applique que dans les cas ou la robustesse est une composante crutiale
(aéronautique, médical, etc.)

### Vérification par les tests

Aussi appelée **vérification pratique**, elle est effectuée par des programmes
qui vérifient ou non des propriété de notre implementation. Attention cependant,
les tests validés ne signifient pas pour autant que notre programme fonctionne.

Il sont rarement exhaustifs et exacts.

La validations d'une batterie de tests ne signifie donc pas que notre programme
fonctionne, cependant si l'un d'eux échoue alors notre programme ne fonctionne
pas. On audugmente juste la confiance en notre implementation.

## Non exhaustivité des tests

Plusieurs facteurs implique la non exhaustivité des tests:

 * La taille et la description des domaines d'entrée;
 * Les caractéristiques générale de notre logiciel par exemple la complexité,
   l'ergonomie, la flexibilité, ...;
 * Les caractéristiques de l'implémentation;
 * Les ressources affectés aux tests.

Les tests sont d'autant plus non exhastif qu'ils couvrent beaucoup d'aspects du
logiciel comme les performances, la cmplexité, l'ergonomie, la préservation des
propriétés après modifications ect. Même **après décomposition** en
sous-besoins, ces points sont tout aussi difficiles à tester, par exemple
l'*ergonomie*:

 * **l'efficacité** - permettre aux utilisateurs d'atteindre leurs buts;
 * **la productivité** - minimiser le temps et l'effort nécessaires à 
   l'utilisateur;
 * **la sûreté de fonctionnement** - limiter les risques pour l'utilisateur;
 * **La satisfaction**.

Il est est difficile de décrire toutes les propriété d'un logiciel et donc de
produire des tests exhastifs. Mais comme nous l'avons évoqué plus haut, les
ressources allouées aus test limitent aussi cette exhastivité (motivatuons des
testeurs, temps, moyens humains etc. ).

### La robustesse d'un programme

Elle se définie par rapport à la spécification des domaines d'entrée 
\\(Dom_{OK}\\) pour lesquel le programme doit fonctionner. Les tests positifs
sont ceux avec les entrées dans \\(Dom_{OK}\\). Ceux négatifs sont donc ceux
pour les entrées hors de \\(Dom_{OK}\\).

Tester le domaine pour lesquelle le programme fonctionner est déjà compliqué,
tester le complementaire à \\(Dom_{OK}\\) l'est encore plus! Mais ces tests sont
important.

### La couverture

La non-exhaustivité des tests induisent la **couverture** : on tend vers cette 
exhaustivité, nous avons plusieurs type de couverture aue nous allons détailler.
Mais là encore il est très difficile d'atteindre l'exhaustivité.

#### Les couverture de **code-sources**

Elle conserne:

  * Le *code sources statiques* qui couvrent les codes sources  de tous les
    composants (les lignes de codes);
  * Le *code sources dynaniques* qui couvrent le code pendant son exécution.

Il existe une terminologie spécifique qui se rapporte à la granularité des
élements testés :

 * Les *tests unitaires* qui concernent les éléments basiques du code
   source comme les fonctions ou les méthodes;
 * Les *test de composants* qui concernent des ensembles d'éléments comme les
   modules ou des classes. Il sont souvent associés à des *tests d'intégrations*
   qui vérifient le lien avec les autres élements du code source;
 * Les *tests systèmes* qui s'appliquent au logiciel.

#### Les couvertures de **test domaines**

Il couvrent les domaines d'entrée du programme programme. 

## Les tests autonomes

Il produit un résultat sous booléene : **passé** / **non passé**. Mais pour
fonctionner les tests autonomes soivent contenir tout le necessaire à leurs
fonctionnement comme les données. Il sont en général préférables: ils sont
utilisables, reproductibles et **facilement utilisables en batterie**.

Ils peuvent utiliser des assertions au lien de renvoyer `true` ou `false`. Il
necessite aussi parfois la création de contextes via les `fixtures setup`. Ces
*fixtures* contiennent les opértions d'initialisation, d'ouverture de fichier
etc.

Il est aussi parfois utile de tester avec des composants incomplet ou non
implementés ou instanciés, on passe alors pas des *mocks objects*, des *fake
objects*, des *dummy objects* ou des *stub*. Tester un composant d'envoi de
courriel ne **doit pas vraiment envoyer** de courriels.

## Tests de non régression

L'evolution d'un logiciels comporte des risques d'introduire de nouveaux bugs
même dans les éléments déjà en place. La progression d'un logicel peut entrainer
la regression de son fonctionnement.

Pour vérifier ces élements, nous pouvons alors créer des **tests de non
régression** en géneral mis en batterie pour vérifier la preservation du
comportement.

## Tests en boite blanche / noire

Les test en **boite noire** [^boitenoire] sont réalisés sans tenir compte de
l'implementation mais seulement de son comportement (la spécification). Ceux en
**boite blanche** tien compte su caractères particulier de l'implémentation. Et
enfin ceux **en boite grise** sont des test en boite noire qui utilisent des
hypothèses et / ou informations sur l'implementation.

Les tests en boite noire peuvent être dédiés à la validation et la vérification
de la spécification. Il peuvent être conçus avant même l'implémentation des
fonctionnalité testés. Il sont en quelque sorte une extention de la
spécification.

[^boirenoire]: on parle aussi de tests fonctionnels ou tests de recette
