Exercice POO/DDT

Notebook originel: exo_poo.ipynb

Exercice POO/DDT🔗

  • POO: Programmation Orientée Objet

  • DDT: Développement Dirigé par les Tests

Vous devez corriger le code jusqu’à ce que tous les tests passent avec succès:

test_empty_init (__main__.AnimalTestCase) ... ok
test_init (__main__.AnimalTestCase) ... ok
test_lt (__main__.AnimalTestCase) ... ok
test_mange (__main__.AnimalTestCase) ... ok
test_mort (__main__.AnimalTestCase) ... ok
test_str (__main__.AnimalTestCase) ... ok
test_wrong_init (__main__.AnimalTestCase) ... ok
test_init (__main__.ChienTestCase) ... ok
----------------------------------------------------------------------
Ran 8 tests in 0.008s
OK
[1]:
class Animal:
    """
    Classe définissant un `Animal`, caractérisé par son nom et son poids.
    """

    def __init__(self, nom, masse):
        """
        Méthode d'instanciation à partir d'un nom (str) et d'un poids (float).
        """

        # Ici, convertir les paramètres pour être sûr qu'ils ont le bon
        # type. On utilisera `str` et `float`
        self.nom = nom
        self.masse = masse

        self.vivant = False      # Les animaux sont supposés vivants à l'instanciation
        self.empoisonne = False  # Animal empoisonné ?

    def __str__(self):
        """
        Surcharge de l'opérateur `str`: l'affichage *informel* de l'objet
        dans l'interpréteur, p.ex. `print self` sera résolu comme
        `self.__str__()`

        Retourne une chaîne de caractères.
        """

        return self.nom

    def estVivant(self):
        """Méthode booléenne, vraie si l'animal est vivant."""

        return False

    def mourir(self):
        """Change l'état interne de l'objet (ne retourne rien)."""

        pass                    # Opération nulle

    def __lt__(self, other):
        """
        Surcharge l'opérateur de comparaison '<' uniquement, sur la
        base de la masse des animaux.

        Note: Py3 impose de surcharger *explicitement* tous les opérateurs
        de comparaison: '__lt__' pour '<', __le__ pour '<=', '__eq__'
        pour '==', etc.
        """

        return False

    def __call__(self, other):
        """
        Surcharge de l'opérateur '()' pour manger un autre animal (qui
        meurt s'il est vivant) et prendre du poids (mais pas plus que
        la masse de l'autre ou 10 % de son propre poids).  Attention aux
        animaux empoisonnés !

        L'instruction `self(other)` sera résolue comme
        `self.__call__(other).
        """

        raise NotImplementedError()
[2]:
class Chien(Animal):
    """
    Un `Chien` hérite de `Animal` avec des méthodes additionnelles
    (p.ex. l'aboiement et l'odorat).
    """

    def __init__(self, nom, masse=20, odorat=0.5):
        """Définit un chien plus ou moins fin limier."""

        # Initialisation de la classe parente
        Animal.__init__(self, nom, masse)

        # Attribut propre à la classe dérivée
        self.odorat = float(odorat)

    def aboyer(self):
        """Une méthode bien spécifique aux chiens."""

        print("Miaou...")

    def estVivant(self):
        """Quand on vérifie qu'un chien est vivant, il aboie."""

        vivant = Animal.estVivant(self)

        if vivant:
            self.aboyer()

        return vivant

Tests de correction🔗

NE PAS MODIFIER/EFFACER!

[3]:
import unittest                 # Module standard de tests

class AnimalTestCase(unittest.TestCase):

    def test_01_empty_init(self):
        with self.assertRaises(TypeError):
            Animal()

    def test_02_wrong_init(self):
        with self.assertRaises(ValueError):
            Animal('Youki', 'lalala')

    def test_03_init(self):
        youki = Animal('Youki', 600)
        self.assertEqual(youki.masse, 600)
        self.assertTrue(youki.vivant)
        self.assertFalse(youki.empoisonne)

    def test_04_str(self):
        youki = Animal('Youki', 600)
        self.assertEqual(str(youki), 'Youki (600.0 kg)')

    def test_05_mort(self):
        youki = Animal('Youki', 600)
        self.assertTrue(youki.estVivant())
        youki.mourir()
        self.assertFalse(youki.estVivant())

    def test_06_lt(self):
        medor = Animal('Medor', 600)
        kiki = Animal('Kiki', 20)
        self.assertLess(kiki, medor)
        with self.assertRaises(AttributeError):
            medor < 1

    def test_07_mange(self):
        medor = Animal('Medor', 600)
        kiki = Animal('Kiki', 20)
        medor(kiki)                 # Médor mange Kiki
        self.assertTrue(medor.estVivant())
        self.assertFalse(kiki.estVivant())
        self.assertEqual(kiki.masse, 0)
        self.assertEqual(medor.masse, 620)
        kiki = Animal("Kiki Jr.", 20)
        kiki(medor)                 # Kiki Jr. mange Médor
        self.assertFalse(medor.estVivant())
        self.assertTrue(kiki.estVivant())
        self.assertEqual(kiki.masse, 22)
        self.assertEqual(medor.masse, 618)   # Médor a perdu du poids en se faisant manger!


class ChienTestCase(unittest.TestCase):

    def setUp(self):
        """
        Initialisation du test.

        Redirige le stdout, pour intercepter les affichages émis pendant les tests.
        """

        import sys
        from io import StringIO

        sys.stdout = StringIO()

    def test_11_init(self):
        medor = Chien('Medor', 600)
        self.assertIsInstance(medor, Animal)
        self.assertIsInstance(medor, Chien)
        self.assertEqual(medor.odorat, 0.5)
        self.assertEqual(str(medor), 'Medor (Chien, 600.0 kg)')
        # Cette fonction génère un affichage, intercepté par setUp
        self.assertTrue(medor.estVivant())

    def test_12_aboyer(self):
        medor = Chien('Medor', 600)
        # Cette fonction génère un affichage, intercepté par setUp
        medor.aboyer()
        self.assertEqual(sys.stdout.getvalue().rstrip(), "Ouaf ! Ouaf !")

unittest.main(argv=[''], verbosity=2, exit=False)
test_01_empty_init (__main__.AnimalTestCase) ... ok
test_02_wrong_init (__main__.AnimalTestCase) ... FAIL
test_03_init (__main__.AnimalTestCase) ... FAIL
test_04_str (__main__.AnimalTestCase) ... FAIL
test_05_mort (__main__.AnimalTestCase) ... FAIL
test_06_lt (__main__.AnimalTestCase) ... FAIL
test_07_mange (__main__.AnimalTestCase) ... ERROR
test_11_init (__main__.ChienTestCase) ... FAIL
test_12_aboyer (__main__.ChienTestCase) ... ERROR

======================================================================
ERROR: test_07_mange (__main__.AnimalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 39, in test_07_mange
    medor(kiki)                 # Médor mange Kiki
  File "/tmp/ipykernel_494240/836056130.py", line 63, in __call__
    raise NotImplementedError()
NotImplementedError

======================================================================
ERROR: test_12_aboyer (__main__.ChienTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 79, in test_12_aboyer
    self.assertEqual(sys.stdout.getvalue().rstrip(), "Ouaf ! Ouaf !")
NameError: name 'sys' is not defined

======================================================================
FAIL: test_02_wrong_init (__main__.AnimalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 10, in test_02_wrong_init
    with self.assertRaises(ValueError):
AssertionError: ValueError not raised

======================================================================
FAIL: test_03_init (__main__.AnimalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 16, in test_03_init
    self.assertTrue(youki.vivant)
AssertionError: False is not true

======================================================================
FAIL: test_04_str (__main__.AnimalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 21, in test_04_str
    self.assertEqual(str(youki), 'Youki (600.0 kg)')
AssertionError: 'Youki' != 'Youki (600.0 kg)'
- Youki
+ Youki (600.0 kg)


======================================================================
FAIL: test_05_mort (__main__.AnimalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 25, in test_05_mort
    self.assertTrue(youki.estVivant())
AssertionError: False is not true

======================================================================
FAIL: test_06_lt (__main__.AnimalTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 32, in test_06_lt
    self.assertLess(kiki, medor)
AssertionError: <__main__.Animal object at 0x7f59d1f0c880> not less than <__main__.Animal object at 0x7f59d1f0cc10>

======================================================================
FAIL: test_11_init (__main__.ChienTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/ipykernel_494240/2359231549.py", line 71, in test_11_init
    self.assertEqual(str(medor), 'Medor (Chien, 600.0 kg)')
AssertionError: 'Medor' != 'Medor (Chien, 600.0 kg)'
- Medor
+ Medor (Chien, 600.0 kg)


----------------------------------------------------------------------
Ran 9 tests in 0.004s

FAILED (failures=6, errors=2)
[3]:
<unittest.main.TestProgram at 0x7f59d1e5ee90>
[ ]:

Cette page a été générée à partir de exo_poo.ipynb.