Adloun

Un groupe présente cette fonction et ses tests

Exercice de TD · niveau 2 · NSI (première), chapitre 11 — Conduire un projet · Tester pour de vrai

Énoncé

Un groupe présente cette fonction et ses tests. Les tests passent.


def moyenne(notes):
    return sum(notes) / len(notes)

def test_moyenne():
    assert moyenne([10, 12, 14]) == 12
    assert moyenne([8, 8, 8]) == 8
    assert moyenne([0, 10, 20]) == 10

Corrigé

1. Un seul. Les trois assertions sont la même : trois notes entières, dont la moyenne tombe juste. Changer les nombres ne change pas le cas — on a écrit trois fois le même test.

C'est le défaut le plus fréquent des jeux de tests d'élèves, et le plus difficile à voir : le compteur affiche « 3 tests », et l'on croit avoir couvert trois situations.

2. Deux appels qui échouent :


moyenne([])            # ZeroDivisionError: division by zero
moyenne(["12", "15"])  # TypeError: unsupported operand type(s)

Le premier est le cas limite qu'aucune des trois assertions n'approchait : une liste vide arrive tôt ou tard, par exemple pour un élève sans note. Le second est le cas d'erreur : des notes lues dans un fichier arrivent sous forme de chaînes de caractères.

3. Il faut d'abord décider ce que la fonction fait d'une liste vide. Deux réponses défendables : renvoyer None, ou refuser l'appel. La seconde est préférable ici — une moyenne de rien n'existe pas, et un None qui circule provoquera une erreur plus loin, à un endroit où l'on ne comprendra plus pourquoi.


def moyenne(notes):
    """Moyenne d'une liste non vide de nombres.
    Precondition : len(notes) > 0."""
    assert len(notes) > 0, "moyenne d'une liste vide"
    return sum(notes) / len(notes)

def test_nominaux():
    assert moyenne([10, 12, 14]) == 12
    assert moyenne([0, 10, 20]) == 10

def test_limites():
    assert moyenne([7]) == 7          # une seule note
    assert moyenne([1, 2]) == 1.5     # moyenne non entiere
    vide = False
    try:
        moyenne([])
    except AssertionError:
        vide = True
    # Le verdict est HORS du try : un assert place dedans serait avale
    # par le except, et le test passerait quoi qu'il arrive.
    assert vide, "aurait du refuser la liste vide"

for test in (test_nominaux, test_limites):
    test()
    print("ok", test.__name__)

Contrôle : le nouveau jeu couvre quatre cas réellement distincts — plusieurs notes, une seule note, moyenne non entière, liste vide — là où l'ancien en couvrait un, avec le même nombre d'assertions.

La question à se poser devant tout jeu de tests : non pas « combien y a-t-il d'assertions ? », mais « si j'en supprimais une, laquelle me manquerait ? ». Dans le jeu de départ, on pouvait en supprimer deux sans rien perdre.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.