Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

  1. Créez un répertoire TP13/, ainsi que les répertoires et fichiers suivants.

TP13/
├── include/
│    ├── student.h
│    ├── grade-book.h
│    ├── input-reader.h
│    └── application.h
├── source/
│    ├── student.cpp
│    ├── grade-book.cpp
│    ├── input-reader.cpp
│    ├── application.cpp
│    └── main.cpp
├── tests/
│    ├── include/
│    |   ├── student-test.h
│    |   └── grade-book-test.h
|    └── source/
|        ├── student-test.cpp
|        ├── grade-book-test.cpp
│        └── main.cpp
├── compile_flags.txt
└── makefile
  1. Remplissez TP13/include/ avec le contenu suivant.

student.h
grade-book.h
input-reader.h
application.h
#ifndef STUDENT_H
#define STUDENT_H

#include <string>
#include <vector>

class Student {
private:
    std::string mName;
    std::vector<double> mGrades;

public:
    Student(const std::string& name);
    std::string getName() const;
    void addGrade(double grade);
    double getAverage() const;
};

#endif
  1. Remplissez TP13/source/ avec le contenu suivant.

student.cpp
grade-book.cpp
input-reader.cpp
application.cpp
main.cpp
#include "student.h"
#include <string>
#include <vector>

Student::Student(const std::string& name) : mName(name) {}

std::string Student::getName() const {
    return mName;
}

void Student::addGrade(double grade) {
    mGrades.push_back(grade);
}

double Student::getAverage() const {
    double sum = 0.0;
    for (double grade : mGrades) {
        sum += grade;
    }

    return sum / mGrades.size();
}
  1. Remplissez TP13/tests/include/ et TP13/tests/source/ avec le contenu suivant.

student-test.h
grade-book-test.h
student-test.cpp
grade-book-test.cpp
main.cpp
#ifndef STUDENT_TEST_H
#define STUDENT_TEST_H

class StudentTest {
public:
    static void runTests();

private:
    static void getAverage_NoGrades_ThrowsRuntimeError();
};

#endif
  1. Remplissez compile_flags.txt et makefile avec le contenu suivant.

compile_flags.txt
makefile
-xc++
-std=c++17
-Iinclude
-Itests/include
  1. Compilez et exécutez le code pour observer son fonctionnement.

  1. Essayez de produire des erreurs en cherchant un étudiant qui n’existe pas dans le carnet de notes ou en affichant la moyenne d’un étudiant sans note.

  2. Rectifiez ces problèmes en levant des exceptions aux bons endroits pour passer les tests.

Les exceptions que nous avons levées viennent d’erreurs récupérables et ne doivent pas interrompre le programme.

  1. Ajoutez des blocs try et catch autour de switch(choice) {...} pour attraper les exceptions levées et afficher leur message d’erreur.

Nous avons toujours le problème des entrées qui ne sont pas validées :

  1. Déclarez les fonctions de validation suivantes dans input-reader.h :

  1. Ajoutez les tests suivants aux bons endroits et appeler InputReaderTest::runTests() dans tests/source/main.cpp.

input-reader-test.h
#ifndef INPUT_READER_TEST_H
#define INPUT_READER_TEST_H

#include <istream>
#include <vector>
#include <string>


class InputReaderTest {
public:
    static void runTests();

private:
    static void assertValidity(bool (*validationFunction)(std::istream&), const std::vector<std::string>& inputStrings, bool expectedValidity);

    static void isValidGrade_DoubleBetween0And20_ReturnsTrue();
    static void isValidGrade_DoubleOutsideAcceptedRange_ReturnsFalse();
    static void isValidGrade_DoubleOutOfRange_ReturnsFalse();
    static void isValidGrade_NonDouble_ReturnsFalse();

    static void isValidMenuChoice_IntegerBetween1And4_ReturnsTrue();
    static void isValidMenuChoice_IntegerOutsideAcceptedRange_ReturnsFalse();
    static void isValidMenuChoice_IntegerOutOfRange_ReturnsFalse();
    static void isValidMenuChoice_NonInteger_ReturnsFalse();

    static void isValidStudentName_NonBlankInput_ReturnsTrue();
    static void isValidStudentName_BlankInput_ReturnsFalse();
};

#endif
input-reader-test.cpp
#include "input-reader.h"
#include "input-reader-test.h"
#include <cassert>
#include <iostream>
#include <sstream>
#include <vector>
#include <string>

void InputReaderTest::runTests() {
    isValidGrade_DoubleBetween0And20_ReturnsTrue();
    isValidGrade_DoubleOutsideAcceptedRange_ReturnsFalse();
    isValidGrade_DoubleOutOfRange_ReturnsFalse();
    isValidGrade_NonDouble_ReturnsFalse();
    isValidMenuChoice_IntegerBetween1And4_ReturnsTrue();
    isValidMenuChoice_IntegerOutsideAcceptedRange_ReturnsFalse();
    isValidMenuChoice_IntegerOutOfRange_ReturnsFalse();
    isValidMenuChoice_NonInteger_ReturnsFalse();
    isValidStudentName_NonBlankInput_ReturnsTrue();
    isValidStudentName_BlankInput_ReturnsFalse();
    std::cout << "All InputReader tests passed\n";
}

void InputReaderTest::assertValidity(bool (*validationFunction)(std::istream&), const std::vector<std::string>& inputStrings, bool expectedValidity) {
    for (const std::string& inputString : inputStrings) {
        std::istringstream stream(inputString);
        bool actualValidity = validationFunction(stream);
        if (actualValidity != expectedValidity) {
            std::cerr << "Failed for input: " << inputString << std::endl;
        }
        assert(actualValidity == expectedValidity);
    }
}

void InputReaderTest::isValidGrade_DoubleBetween0And20_ReturnsTrue() {
    assertValidity(InputReader::isValidGrade, {"0", "20", "10.5", "   15.67 "}, true);
    std::cout << "isValidGrade_DoubleBetween0And20_ReturnsTrue passed\n";
}

void InputReaderTest::isValidGrade_DoubleOutsideAcceptedRange_ReturnsFalse() {
    assertValidity(InputReader::isValidGrade, {"-11.55", "22.11"}, false);
    std::cout << "isValidGrade_DoubleOutsideAcceptedRange_ReturnsFalse passed\n";
}

void InputReaderTest::isValidGrade_DoubleOutOfRange_ReturnsFalse() {
    assertValidity(InputReader::isValidGrade, {"999999999999999999999999999999999999999"}, false);
    std::cout << "isValidGrade_DoubleOutOfRange_ReturnsFalse passed\n";
}

void InputReaderTest::isValidGrade_NonDouble_ReturnsFalse() {
    assertValidity(InputReader::isValidGrade, {"abc", "12abc", "", "   "}, false);
    std::cout << "isValidGrade_NonDouble_ReturnsFalse passed\n";
}

void InputReaderTest::isValidMenuChoice_IntegerBetween1And4_ReturnsTrue() {
    assertValidity(InputReader::isValidMenuChoice, {"1", "2", "3", "  4 "}, true);
    std::cout << "isValidMenuChoice_IntegerBetween1And4_ReturnsTrue passed\n";
}

void InputReaderTest::isValidMenuChoice_IntegerOutsideAcceptedRange_ReturnsFalse() {
    assertValidity(InputReader::isValidMenuChoice, {"0", "5"}, false);
    std::cout << "isValidMenuChoice_IntegerOutsideAcceptedRange_ReturnsFalse passed\n";
}

void InputReaderTest::isValidMenuChoice_IntegerOutOfRange_ReturnsFalse() {
    assertValidity(InputReader::isValidMenuChoice, {"999999999999999999999999999999999999999"}, false);
    std::cout << "isValidMenuChoice_IntegerOutOfRange_ReturnsFalse passed\n";
}

void InputReaderTest::isValidMenuChoice_NonInteger_ReturnsFalse() {
    assertValidity(InputReader::isValidMenuChoice, {"abc", "1abc", "", "1.5"}, false);
    std::cout << "isValidMenuChoice_NonInteger_ReturnsFalse passed\n";
}

void InputReaderTest::isValidStudentName_BlankInput_ReturnsFalse() {
    assertValidity(InputReader::isValidStudentName, {"", "   "}, false);
    std::cout << "isValidStudentName_BlankInput_ReturnsFalse passed\n";
}

void InputReaderTest::isValidStudentName_NonBlankInput_ReturnsTrue() {
    assertValidity(InputReader::isValidStudentName, {"A", "Alice", "Alice B", "   XA 13 "}, true);
    std::cout << "isValidStudentName_NonBlankInput_ReturnsTrue passed\n";
}
  1. Implémentez les fonctions isValidGrade, isValidMenuChoice et isValidStudentName dans input-reader.cpp, comme vu en cours, pour passer les tests.

  1. Ajoutez la fonction privée requestInput à InputReader, comme vu en cours, pour limiter le nombre de tentatives d’entrée à 5, par exemple.

  2. Refactorisez les fonctions requestMenuChoice, requestGrade et requestStudentName de input-reader.cpp en utilisant requestInput et les fonctions de validation, comme vu en cours.

  3. Compilez, exécutez votre code et vérifiez que les entrées non valides affichent des messages d’erreur bien choisis sans interrompre le programme.

  4. Ajoutez un dernier bloc try-catch pour les Fatal error dans source/main.cpp, qui attrape toutes les autres exceptions (y compris std::invalid_argument("Maximum number of attempts reached.") de InputReader::requestInput), comme vu en cours.

  5. Compilez et exécutez votre code une dernière fois. Est-ce que votre programme est robuste ?

À retenir
  • Lever (throw, syntaxe) des exceptions avec des types précis (comme std::invalid_argument ou std::runtime_error).

  • Utiliser try (syntaxe) uniquement autour du code qui peut lever des exceptions.

  • Attraper (catch, syntaxe) des exceptions avec des types précis sans interrompre le programme lorsqu’une erreur est récupérable.

  • Valider les entrées avec des fonctions de validation appropriées (syntaxe).

  • Limiter le nombre de tentatives d’entrée pour éviter les boucles infinies (avec requestInput).

  • Attraper les Fatal errors dans source/main.cpp (syntaxe).

  • Écrire un test unitaire pour une fonction qui peut lever une exception (syntaxe).