Plus facile à lire avec un fond crème, une teinte, plus de contraste ou un texte plus grand ? Choisissez vos couleurs et votre texte, une fois pour toutes les pages.

Leçon gratuite

La couverture de code

Après cette leçon: Je peux lire un taux de couverture et dire ce qu’il ne montre pas.

Leçon de 55 min7 commandesLab Linux du quotidien : la trentaine de commandes que les ingénieurs tapent de tête

Qu’est-ce que la couverture de code ?

La couverture de code est la part des lignes, ou des branches, d’un programme que les tests ont exécutées. Elle montre le code qu’aucun test n’a jamais lancé, mais elle ne dit pas si les tests vérifient quelque chose.

Une équipe annonce « 100 % de couverture » et se croit à l’abri. Cette leçon montre pourquoi ce chiffre rassure plus qu’il ne prouve.

À la fin de cette leçon

Remarque

Je peux lire un taux de couverture et dire ce qu’il ne montre pas.

Mots-clés

MotCe que ça veut direExemple
Couverture de lignesla part des lignes exécutées au moins une fois par les tests
Brancheun des deux chemins d’un if : la condition vraie, ou fausse
Couverture de branchesla part des branches prises au moins une fois par les tests
Tracela liste des lignes qu’un test a exécutées, dans l’ordre
Taux de couverturele pourcentage de lignes ou de branches exécutées5 lignes sur 6, soit 83 %
Ligne non couverteune ligne qu’aucun test n’a exécutée

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

La couverture de lignes compte les lignes exécutées

Un outil de couverture suit les tests et note chaque ligne qu’ils exécutent. À la fin, il divise le nombre de lignes exécutées par le nombre de lignes exécutables.

Prenons une petite fonction qui applique une remise aux clients qui ont une carte de fidélité. Le tableau suit deux tests.

Une fonction de quatre lignes

Code: python
def prix_final(prix, carte):
    if carte:
        prix = prix - prix // 10
    return prix

# Test A : avec carte. Test B : sans carte.
assert prix_final(1000, True) == 900
assert prix_final(1000, False) == 1000
print("Les deux chemins sont essayés")

Exemple travaillé : les lignes exécutées

LigneCodeTest A : carte vraieTest B : carte fausse
1def prix_final(prix, carte):ouioui
2if carte:ouioui
3prix = prix - prix // 10ouinon
4return prixouioui

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Calculer un taux de couverture

Le test A seul exécute les lignes 1, 2, 3 et 4 : quatre lignes sur quatre, soit 100 %. Le test B seul n’exécute pas la ligne 3 : trois lignes sur quatre, soit 75 %.

La formule est simple : lignes exécutées × 100, divisé par lignes exécutables. Avec 5 lignes sur 6, on obtient 83 %.

Les branches montrent ce que les lignes cachent

Le test A seul exécute toutes les lignes, mais il ne fait jamais passer carte à faux. Le chemin « sans carte » n’est jamais pris.

Un if a deux branches : la condition vraie et la condition fausse. Le test A prend une branche sur deux : la couverture de branches est de 50 %, alors que celle des lignes est de 100 %.

Exemple travaillé : lignes contre branches

Tests lancésLignes couvertesBranches couvertes
A seul (carte vraie)4 sur 4, soit 100 %1 sur 2, soit 50 %
B seul (carte fausse)3 sur 4, soit 75 %1 sur 2, soit 50 %
A et B4 sur 4, soit 100 %2 sur 2, soit 100 %

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Écrire la fonction de fidélité

Commande
printf 'def prix_final(prix, carte):\n    if carte:\n        prix = prix - prix // 10\n    return prix\n' > essais/couv.py

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire le test A seul

Commande
printf 'import unittest\nfrom couv import prix_final\n\n\nclass TestsCouv(unittest.TestCase):\n    def test_avec_carte(self):\n        self.assertEqual(prix_final(1000, True), 900)\n' > essais/test_couv.py

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Lancer le test A : vert, 100 % des lignes

Commande
python3 -m unittest discover -s essais -p "test_couv.py" 2>&1 | tail -1

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Une branche reste sans test

Le résultat est OK, et toutes les lignes sont exécutées. Pourtant la fonction n’a jamais été essayée sans carte.

Si quelqu’un casse ce chemin demain, rien ne sera rouge. La commande qui suit essaie ce chemin à la main, comme le ferait le test B.

Essayer le chemin sans carte

Commande
PYTHONPATH=essais python3 -c "from couv import prix_final; print(prix_final(1000, False))"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Un test à 100 % peut ne rien vérifier

La couverture compte les lignes exécutées, pas les lignes vérifiées. Un test qui appelle la fonction sans rien comparer couvre tout, et ne prouve rien.

Écrivons une fonction fausse, qui rend toujours 0, et un test qui l’appelle sans assertion.

Écrire une fonction fausse

Commande
printf 'def prix_final(prix, carte):\n    if carte:\n        prix = 0\n    return 0\n' > essais/couv_faux.py

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire un test sans assertion

Commande
printf 'import unittest\nfrom couv_faux import prix_final\n\n\nclass TestsVide(unittest.TestCase):\n    def test_appel_sans_verifier(self):\n        prix_final(1000, True)\n' > essais/test_couv_faux.py

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Le test est vert, la fonction est fausse

Commande
python3 -m unittest discover -s essais -p "test_couv_faux.py" 2>&1 | tail -1

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Erreur fréquente

Attention

Beaucoup pensent que 100 % de couverture veut dire un programme sans faute. Ce chiffre dit seulement que chaque ligne a tourné une fois. Il ne dit rien sur les assertions, ni sur les valeurs essayées.

Calculer la couverture à partir de traces

Dans le devoir, une trace est la liste des numéros de lignes qu’un test a exécutées. Vous recevez les lignes exécutables et une liste de traces, une par test.

Une ligne est couverte si au moins une trace la contient. couverture(total, traces) rend le pourcentage entier, arrondi vers le bas. lignes_manquantes(total, traces) rend les lignes jamais exécutées, dans l’ordre.

À propos de ce labo et de sa source: Lab Linux du quotidien : la trentaine de commandes que les ingénieurs tapent de tête

Vous enseignez à une classe ? Proposez ce module à vos étudiants avec un guide de l'enseignant, des devoirs corrigés et un laboratoire pour chaque étudiant.