fiche mémo1 min de lecture

Un contrat, trois écritures

Mémo de révision : docstring, jeu de tests écrit avant le code, assertion de précondition.
programme

En une phrase

Un contrat s'écrit trois fois : en français dans la docstring, en assert dans un jeu de tests écrit avant le code, en assert dans la méthode pour refuser un appel interdit.

À mémoriser

  • Docstring : le contrat écrit dans le code ; help(Pile.depiler) l'affiche sans ouvrir le corps.
  • Précondition : ce que l'appel doit garantir avant ; postcondition : ce que la méthode promet après.
  • Une promesse du contrat, un assert : c'est ainsi que s'écrit un jeu de tests.
  • Le jeu de tests ne dépend que du contrat, donc il s'écrit avant le code.
  • Un bon jeu de tests échoue tant que le code n'existe pas et passe une fois qu'il est juste : il définit « fini ».
  • Le succès d'un jeu de tests ne garantit pas la correction d'un programme, seulement celle des cas écrits.
  • Le test vit dehors et accuse l'implémentation ; l'assertion vit dedans et accuse l'appelant.

À savoir reconnaître

SituationCe que c'est
assert p.depiler() == "R" après empiler(7), empiler("R")un test : dehors, accuse l'implémentation
assert not self.est_vide() en première ligne de depilerune précondition : dedans, accuse l'appelant
assert len(p) == 2 juste après p.sommet()test d'une absence d'effet : sommet ne retire rien
Le test échoue sur une classe sans corpsnormal : il liste ce qu'il reste à écrire
Une implémentation fausse passe le testun cas manque au jeu de tests
IndexError: pop from empty listmessage qui parle des entrailles, pas du contrat : précondition non écrite

Pièges courants

  • Tester l'implémentation (p.elements == [...]) au lieu du contrat.
  • N'empiler qu'un élément pour tester LIFO : une file passerait aussi.
  • Conclure qu'un code est juste parce que ses tests passent.
  • assert (condition, "message") : un tuple non vide est toujours vrai, l'assertion ne saute jamais.
  • Ne rien écrire sur depiler() ou sommet() d'une pile vide : le code décide alors à la place du contrat.
  • Compter sur assert pour la sécurité : python -O les désactive toutes.
  • Le jeu de tests se relance à chaque modification, pas une seule fois.

Syntaxe Python

class Pile:
    def depiler(self):
        """Retirer l'élément du sommet et le renvoyer.
        Précondition : la pile n'est pas vide."""
        assert not self.est_vide(), "depiler exige une pile non vide"
        return self.elements.pop()


def tester_pile(fabriquer_une_pile):
    p = fabriquer_une_pile()
    assert p.est_vide()
    p.empiler(7)
    p.empiler("R")
    assert p.sommet() == "R"
    assert len(p) == 2            # sommet ne retire rien
    assert p.depiler() == "R"
    assert p.depiler() == 7
    assert p.est_vide()