Test logiciel

Plan

  • Introduction

  • Différents types de test

  • Test unitaire

  • Test unitaire avec Alsatian

  • Conclusion

Test logiciel

Processus d’analyse d’un logiciel pour détecter des différences entre les conditions existantes et exigées (c’est à dire, un bug) et d’évaluer les propriétés du logiciel.

— ISO/IEC/IEEE 29119 Software Testing
bug

Fiabilité

Capacité d’un produit logiciel à effectuer les fonctions requises dans les conditions spécifiées pour des périodes de temps spécifiées, ou pour un nombre spécifique d’opérations

— ISO 9126

Vérification: le logiciel doit être conforme à sa spécification.

— Construisons-nous le produit correctement?

monopoly

— Je suis tombé sur la case «Départ», mais je n’ai pas reçu mes $200

Validation: le logiciel doit remplir les exigences de l’utilisateur.

— Construisons-nous le bon produit?

game of life

— Ce jeu contient bien des cases, des joueurs et une case «Départ», mais ce n’est pas le jeu que je voulais.

Vérification par le test

  • Vérification

    • Contrôler que le programme sous test respecte la spécification

  • Test

    • Principe générale : faire des essais

    • Combien ? De quel type ? A quel point ?

    • En opposition avec la preuve qui consiste à formaliser le système pour appliquer des vérifications de niveau mathématique

      • Difficulté de la formalisation

Test et preuve

preuve test

Motivation

problematique test

Vérifier pour prévenir des erreurs

  • Une erreur est une décision inappropriée ou erronée, faite par un développeur, qui conduit à l’introduction d’un défaut.

  • Un défaut est une imperfection dans un des aspects du système qui contribue, ou peut potentiellement contribuer, à la survenance d’une ou de plusieurs défaillances

    • Parfois, il faut plusieurs défauts pour causer une défaillance.

  • Une défaillance est un comportement inacceptable présenté par un système.

    • La fréquence des défaillances reflète la fiabilité.

Types d’erreurs

  • Erreur de code.

  • Erreur de processus.

  • Erreur de documentation.

  • Erreur de données.

Causes principales d’erreurs

  • Mauvaise spécification des exigences logicielles: erronées, incomplètes, inconsistantes.

  • Erreurs de conception: mauvais choix de conception

  • Erreurs de mise en œuvre: erreurs d’opérateurs, oublis, expressions incorrectes.

  • Outils inadaptés ou fragiles: langages de programmation, compilateurs, bibliothèques, outils de développement.

  • Lacunes dans le processus de test: tests incomplets, vérification inadéquates, erreurs de déboggage.

  • Erreurs d’évolution: maintenance peu soignée, ajout de nouvelles erreurs, complexité progressive.

  • Écarts délibérés des exigences.

  • Non-respect des règles de codage et de documentation.

Zune bug (2008)

  • Le 31 décembre 2008, tous les dispositifs Zune se sont arrêtés.

  • Cause: une boucle infinie dans la gestion d’années bissextiles.

  • Solution: attendre le lendemain.

zune
 year = ORIGINYEAR;
    while (days > 365)
    {
        if (IsLeapYear(year))
        {
            if (days > 366)
            {
                days -= 366;
                year += 1;
            }
        }
        else
        {
            days -= 365;
            year += 1;
        }
    }

Plan

  • Introduction

  • Différents types de test

  • Test unitaire

  • Test unitaire avec Alsatian

  • Conclusion

Classement des tests

Différents critères:
Le facteur de qualité testée

fonctionnel, performance, etc.

L’échelle

unitaire, intégration, système, etc.

La phase de développement

non-régression, acceptation, etc.

Le type d’exécution

dynamique, statique.

La génération des données de test

fonctionnel, structurel

Classement selon facteur de qualité testé

Facteur de qualité testé

  • Fonctionnalité

  • Sécurité, intégrité

  • Utilisabilité

  • Cohérence

  • Maintenabilité

  • Efficacité

  • Robustesse

  • Sûreté de fonctionnement

  • Performance (charge, passage à l’échelle, stress)

  • Portabilité

  • Etc.

Classement selon l’échelle

Types de test selon l’échelle

Unitaire

Test individuel d’une unité: composant, classe, procédure/fonction.

Intégration

Choix d’un ordre pour intégrer et tester les différentes unités du système

Système

Test de la globalité du système, des fonctions offertes, à partir de l’interface

Test unitaire

  • Vérification d’une unité indépendamment des autres

  • Vérifier intensivement les fonctions unitaires

unit

Test unitaire

  • Pour un langage procédural: unité de test = procédure

function isLeapYear(year: number): boolean {
    if (year % 400 == 0 && year % 100 == 0) {
        return true;
    }

    if (year % 100 == 0) {
        return false;
    }

    return (year % 4 == 0);
}

Test d’intégration

  • Étape du test logiciel pendant laquelle les unités sont réunies et testés comme un groupe.

  • Elle se passe après le test unitaire et avant le test système.

  • L’objectif du test d’intégration est de tester les interactions entre les unités.

integration

Test système

  • Test d’un système intégré pour vérifier s’il répond aux exigences spécifiées.

systeme

Classement selon la phase de développement

Types de test selon l’étape du cycle de vie logiciel

  • Alpha

  • Smoke

  • Nouvelle fonctionnalité

  • Non régression

  • Acceptation

  • Beta

Test de non régression

  • Consiste à vérifier que des modifications apportées au logiciel n’ont pas introduit de nouvelle erreur

    • vérifier que ce qui marchait marche encore

  • Dans la phase de maintenance du logiciel

    • Après refactoring, ajout/suppression de fonctionnalités

    • Après la correction d’une faute

Classement selon le type d’exécution

Type d’exécution

Test statique
  • relecture / revue de code

  • analyse automatique

  • vérification de propriétés, règles de codage…

Test dynamique
  • on exécute le programme avec des données en entrée et on observe le comportement

Processus du test dynamique

test dynamique

Données du test dynamique

test dynamique
  • Données de test en entrée du programme sous test

  • Données de sortie produites par le programme

  • Verdict

Activités du test dynamique

test dynamique
  • Création des données de test

    • Génération

    • Qualification

  • Oracle

    • Analyse des données de sortie

    • Confrontation avec la spécification

  • Critère de test

    • A-t-on assez testé ?

    • Localisation/correction des erreurs

Classement selon la génération des données de test

Génération des données de test

boite noire
  • Test fonctionnel (test boîte noire)

  • Utilise la description des fonctionnalités du programme

boite blanche
  • Test structurel (test boîte blanche)

  • Utilise la structure interne du programme

Test fonctionnel

function isLeapYear(year: number): boolean {
Données de test:
  • Années bissextiles: 1972, 2000, 2008

  • Années non-bissextiles: 2017, 1999, 1969

Test structurel

  • A partir d’un modèle du code

    • modèle de contrôle (conditionnelles, boucles…)

    • modèle de données

    • modèle de flot de données (définition, utilisation…)

  • Utilisation importante des parcours de graphes

    • critères basés sur la couverture du code

Test structurel

function isLeapYear(year: number): boolean {
    if (year % 400 == 0 && year % 100 == 0) {
        return true;
    }

    if (year % 100 == 0) {
        return false;
    }

    return (year % 4 == 0);
}
Données de test:
  • Premier opérateur conditionnel: 2000

  • Deuxième opérateur conditionnel: 2100

  • Dernière instruction: 2004

cfg leapyear

Plan

  • Introduction

  • Différents types de test

  • Test unitaire

  • Test unitaire avec Alsatian

  • Conclusion

Test unitaire

Fonctionnel et dynamique

Objectifs

  • Vérifier que chaque unité fonctionne comme conçue.

  • Isoler chaque partie d’un logiciel et montrer qu’elles sont correctes.

Postulat

Une fonction sans test unitaire ne fonctionne pas

Conséquences des tests unitaires

  • Évitent les print() intempestifs

  • Accélèrent le développement

  • Évitent le déboggage

  • Simplifient et améliorent la conception

  • Documentation "vivante" du logiciel

  • Spécifient le comportement attendu

  • Réduisent les coûts futurs.

  • Simplifient l’évolution

    • Évitent la régression

    • Les tests servent de «filet de sécurité» aux modifications

  • Simplifient l’intégration

    • Si on fait confiance à chaque unité, les erreurs d’intégration sont plus simples à trouver

Propriétés testées

  • Comportement nominal

  • Gestion d’erreurs

  • Vérification des valeurs d’entrée (paramètres)

  • Correction des valeurs de sortie (valeur de retour)

  • Performance

Caractéristiques d’un bon test unitaire

Isolable

un test unitaire ne doit pas dépendre de l’exécution d’un autre cas de test.

Reproductible

l’exécution d’un cas de test doit toujours produit un même résultat, si le logiciel sous test reste inchangé.

Automatisable

un test unitaire doit pouvoir être exécuté automatiquement.

Simple

un test unitaire ne doit vérifier qu’une seule propriété.

Exemple

function isLeapYear(year: number): boolean
Spécification:
  • Cette fonction retourne vrai si l’entier passé en paramètre est une année bissextile.

  • Seulement valable pour les années ≥ 1900

Test unitaires:
  • Année invalide

  • Une ou plusieurs années bissextiles: 2004, 1996, 2020

  • Une ou plusieurs années non-bissextiles: (1997, 2017)

Plan

  • Introduction

  • Différents types de test

  • Test unitaire

  • Test unitaire avec Alsatian

  • Conclusion

Alsatian

alsatian mascot logo
  • Outil de test unitaire pour Javascript et TypeScript.

Alsatian (Suite)

  • Syntaxe simple et expressive:

Un test unitaire:
@TestCase(2001)
@TestCase(1997)
@Test("Expect odd Years not to be leap")
public oddYearsAreNotLeap(year: number) {
  Expect(isLeapYear(year)).not.toBeTruthy();
}
Une suite de tests:
@TestFixture("Leap Year Unit Tests")
export class LeapYearTest {

    @TestCase(2001)
    @TestCase(1997)
    @Test("Expect odd Years not to be leap")
    public oddYearsAreNotLeap(year: number) {
      Expect(isLeapYear(year)).not.toBeTruthy();
    }

    @TestCase(1996)
    @TestCase(2004)
    @Test("Known Leap Years")
    public expectToBeLeap(year: number) {
      Expect(isLeapYear(year)).toBeTruthy();
    }

    @TestCase(2000)
    @Test("Controversial Years")
    public shouldNotBeLeap(year: number) {
      Expect(isLeapYear(year)).not.toBeTruthy();
    }
}

Plan

  • Introduction

  • Différents types de test

  • Test unitaire

  • Test unitaire avec Alsatian

  • Conclusion

If debugging is the process of removing bugs, then programming must be the process of putting them in

— Edsger W. Dijkstra

Conclusion

  • Il est impossible de tester complètement un logiciel

  • Les tests ne peuvent pas démontrer l’absence d’erreurs

  • Le logiciel évolue continuellement: les tests ne sont pas toujours valides

Les test unitaires sont aussi du code !

Ils doivent être simples à maintenir:
  • être bien nommés

  • être bien commentés

  • avoir un flot de contrôle simple

Les commentaires doivent expliquer:
  • la propriété testée

  • l’état du logiciel dans lequel la propriété doit être testée

  • le résultat attendu

There is always one more bug

— Lubarsky's Law of Cybernetic Entomology