{ "cells": [ { "cell_type": "markdown", "id": "63254074", "metadata": {}, "source": [ "# Exercice POO/DDT\n", "\n", "* POO: Programmation Orientée Objet\n", "* DDT: Développement Dirigé par les Tests\n", "\n", "Vous devez corriger le code jusqu'à ce que tous les tests passent avec\n", "succès:\n", "\n", "```\n", "test_empty_init (__main__.AnimalTestCase) ... ok\n", "test_init (__main__.AnimalTestCase) ... ok\n", "test_lt (__main__.AnimalTestCase) ... ok\n", "test_mange (__main__.AnimalTestCase) ... ok\n", "test_mort (__main__.AnimalTestCase) ... ok\n", "test_str (__main__.AnimalTestCase) ... ok\n", "test_wrong_init (__main__.AnimalTestCase) ... ok\n", "test_init (__main__.ChienTestCase) ... ok\n", "----------------------------------------------------------------------\n", "Ran 8 tests in 0.008s\n", "OK\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "id": "c747c482", "metadata": {}, "outputs": [], "source": [ "class Animal:\n", " \"\"\"\n", " Classe définissant un `Animal`, caractérisé par son nom et son poids.\n", " \"\"\"\n", "\n", " def __init__(self, nom, masse):\n", " \"\"\"\n", " Méthode d'instanciation à partir d'un nom (str) et d'un poids (float).\n", " \"\"\"\n", "\n", " # Ici, convertir les paramètres pour être sûr qu'ils ont le bon\n", " # type. On utilisera `str` et `float`\n", " self.nom = nom\n", " self.masse = masse\n", "\n", " self.vivant = False # Les animaux sont supposés vivants à l'instanciation\n", " self.empoisonne = False # Animal empoisonné ?\n", "\n", " def __str__(self):\n", " \"\"\"\n", " Surcharge de l'opérateur `str`: l'affichage *informel* de l'objet\n", " dans l'interpréteur, p.ex. `print self` sera résolu comme\n", " `self.__str__()`\n", "\n", " Retourne une chaîne de caractères.\n", " \"\"\"\n", "\n", " return self.nom\n", "\n", " def estVivant(self):\n", " \"\"\"Méthode booléenne, vraie si l'animal est vivant.\"\"\"\n", "\n", " return False\n", "\n", " def mourir(self):\n", " \"\"\"Change l'état interne de l'objet (ne retourne rien).\"\"\"\n", "\n", " pass # Opération nulle\n", "\n", " def __lt__(self, other):\n", " \"\"\"\n", " Surcharge l'opérateur de comparaison '<' uniquement, sur la\n", " base de la masse des animaux.\n", "\n", " Note: Py3 impose de surcharger *explicitement* tous les opérateurs\n", " de comparaison: '__lt__' pour '<', __le__ pour '<=', '__eq__'\n", " pour '==', etc.\n", " \"\"\"\n", "\n", " return False\n", "\n", " def __call__(self, other):\n", " \"\"\"\n", " Surcharge de l'opérateur '()' pour manger un autre animal (qui\n", " meurt s'il est vivant) et prendre du poids (mais pas plus que\n", " la masse de l'autre ou 10 % de son propre poids). Attention aux\n", " animaux empoisonnés !\n", "\n", " L'instruction `self(other)` sera résolue comme\n", " `self.__call__(other).\n", " \"\"\"\n", "\n", " raise NotImplementedError()" ] }, { "cell_type": "code", "execution_count": 2, "id": "5718ab5a", "metadata": {}, "outputs": [], "source": [ "class Chien(Animal):\n", " \"\"\"\n", " Un `Chien` hérite de `Animal` avec des méthodes additionnelles\n", " (p.ex. l'aboiement et l'odorat).\n", " \"\"\"\n", "\n", " def __init__(self, nom, masse=20, odorat=0.5):\n", " \"\"\"Définit un chien plus ou moins fin limier.\"\"\"\n", "\n", " # Initialisation de la classe parente\n", " Animal.__init__(self, nom, masse)\n", "\n", " # Attribut propre à la classe dérivée\n", " self.odorat = float(odorat)\n", "\n", " def aboyer(self):\n", " \"\"\"Une méthode bien spécifique aux chiens.\"\"\"\n", "\n", " print(\"Miaou...\")\n", "\n", " def estVivant(self):\n", " \"\"\"Quand on vérifie qu'un chien est vivant, il aboie.\"\"\"\n", "\n", " vivant = Animal.estVivant(self)\n", "\n", " if vivant:\n", " self.aboyer()\n", "\n", " return vivant" ] }, { "cell_type": "markdown", "id": "d7bf2255", "metadata": {}, "source": [ "## Tests de correction\n", "\n", "**NE PAS MODIFIER/EFFACER!**" ] }, { "cell_type": "code", "execution_count": 3, "id": "497f8632", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "test_01_empty_init (__main__.AnimalTestCase) ... ok\n", "test_02_wrong_init (__main__.AnimalTestCase) ... FAIL\n", "test_03_init (__main__.AnimalTestCase) ... FAIL\n", "test_04_str (__main__.AnimalTestCase) ... FAIL\n", "test_05_mort (__main__.AnimalTestCase) ... FAIL\n", "test_06_lt (__main__.AnimalTestCase) ... FAIL\n", "test_07_mange (__main__.AnimalTestCase) ... ERROR\n", "test_11_init (__main__.ChienTestCase) ... FAIL\n", "test_12_aboyer (__main__.ChienTestCase) ... ERROR\n", "\n", "======================================================================\n", "ERROR: test_07_mange (__main__.AnimalTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 39, in test_07_mange\n", " medor(kiki) # Médor mange Kiki\n", " File \"/tmp/ipykernel_494240/836056130.py\", line 63, in __call__\n", " raise NotImplementedError()\n", "NotImplementedError\n", "\n", "======================================================================\n", "ERROR: test_12_aboyer (__main__.ChienTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 79, in test_12_aboyer\n", " self.assertEqual(sys.stdout.getvalue().rstrip(), \"Ouaf ! Ouaf !\")\n", "NameError: name 'sys' is not defined\n", "\n", "======================================================================\n", "FAIL: test_02_wrong_init (__main__.AnimalTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 10, in test_02_wrong_init\n", " with self.assertRaises(ValueError):\n", "AssertionError: ValueError not raised\n", "\n", "======================================================================\n", "FAIL: test_03_init (__main__.AnimalTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 16, in test_03_init\n", " self.assertTrue(youki.vivant)\n", "AssertionError: False is not true\n", "\n", "======================================================================\n", "FAIL: test_04_str (__main__.AnimalTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 21, in test_04_str\n", " self.assertEqual(str(youki), 'Youki (600.0 kg)')\n", "AssertionError: 'Youki' != 'Youki (600.0 kg)'\n", "- Youki\n", "+ Youki (600.0 kg)\n", "\n", "\n", "======================================================================\n", "FAIL: test_05_mort (__main__.AnimalTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 25, in test_05_mort\n", " self.assertTrue(youki.estVivant())\n", "AssertionError: False is not true\n", "\n", "======================================================================\n", "FAIL: test_06_lt (__main__.AnimalTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 32, in test_06_lt\n", " self.assertLess(kiki, medor)\n", "AssertionError: <__main__.Animal object at 0x7f59d1f0c880> not less than <__main__.Animal object at 0x7f59d1f0cc10>\n", "\n", "======================================================================\n", "FAIL: test_11_init (__main__.ChienTestCase)\n", "----------------------------------------------------------------------\n", "Traceback (most recent call last):\n", " File \"/tmp/ipykernel_494240/2359231549.py\", line 71, in test_11_init\n", " self.assertEqual(str(medor), 'Medor (Chien, 600.0 kg)')\n", "AssertionError: 'Medor' != 'Medor (Chien, 600.0 kg)'\n", "- Medor\n", "+ Medor (Chien, 600.0 kg)\n", "\n", "\n", "----------------------------------------------------------------------\n", "Ran 9 tests in 0.004s\n", "\n", "FAILED (failures=6, errors=2)\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import unittest # Module standard de tests\n", "\n", "class AnimalTestCase(unittest.TestCase):\n", "\n", " def test_01_empty_init(self):\n", " with self.assertRaises(TypeError):\n", " Animal()\n", "\n", " def test_02_wrong_init(self):\n", " with self.assertRaises(ValueError):\n", " Animal('Youki', 'lalala')\n", "\n", " def test_03_init(self):\n", " youki = Animal('Youki', 600)\n", " self.assertEqual(youki.masse, 600)\n", " self.assertTrue(youki.vivant)\n", " self.assertFalse(youki.empoisonne)\n", "\n", " def test_04_str(self):\n", " youki = Animal('Youki', 600)\n", " self.assertEqual(str(youki), 'Youki (600.0 kg)')\n", "\n", " def test_05_mort(self):\n", " youki = Animal('Youki', 600)\n", " self.assertTrue(youki.estVivant())\n", " youki.mourir()\n", " self.assertFalse(youki.estVivant())\n", "\n", " def test_06_lt(self):\n", " medor = Animal('Medor', 600)\n", " kiki = Animal('Kiki', 20)\n", " self.assertLess(kiki, medor)\n", " with self.assertRaises(AttributeError):\n", " medor < 1\n", "\n", " def test_07_mange(self):\n", " medor = Animal('Medor', 600)\n", " kiki = Animal('Kiki', 20)\n", " medor(kiki) # Médor mange Kiki\n", " self.assertTrue(medor.estVivant())\n", " self.assertFalse(kiki.estVivant())\n", " self.assertEqual(kiki.masse, 0)\n", " self.assertEqual(medor.masse, 620)\n", " kiki = Animal(\"Kiki Jr.\", 20)\n", " kiki(medor) # Kiki Jr. mange Médor\n", " self.assertFalse(medor.estVivant())\n", " self.assertTrue(kiki.estVivant())\n", " self.assertEqual(kiki.masse, 22)\n", " self.assertEqual(medor.masse, 618) # Médor a perdu du poids en se faisant manger!\n", "\n", "\n", "class ChienTestCase(unittest.TestCase):\n", "\n", " def setUp(self):\n", " \"\"\"\n", " Initialisation du test.\n", "\n", " Redirige le stdout, pour intercepter les affichages émis pendant les tests.\n", " \"\"\"\n", "\n", " import sys\n", " from io import StringIO\n", "\n", " sys.stdout = StringIO()\n", "\n", " def test_11_init(self):\n", " medor = Chien('Medor', 600)\n", " self.assertIsInstance(medor, Animal)\n", " self.assertIsInstance(medor, Chien)\n", " self.assertEqual(medor.odorat, 0.5)\n", " self.assertEqual(str(medor), 'Medor (Chien, 600.0 kg)')\n", " # Cette fonction génère un affichage, intercepté par setUp\n", " self.assertTrue(medor.estVivant())\n", " \n", " def test_12_aboyer(self):\n", " medor = Chien('Medor', 600)\n", " # Cette fonction génère un affichage, intercepté par setUp\n", " medor.aboyer()\n", " self.assertEqual(sys.stdout.getvalue().rstrip(), \"Ouaf ! Ouaf !\")\n", "\n", "unittest.main(argv=[''], verbosity=2, exit=False)" ] }, { "cell_type": "code", "execution_count": null, "id": "989a4037", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" }, "nbsphinx": { "orphan": true }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 5 }