You are here: Sommaire > Plongez au coeur de Python > Refactorisation | << >> | ||||
Plongez au coeur de PythonDe débutant à expert |
Malgré tous vos efforts pour écrire des tests unitaires exhaustifs, vous aurez à faire face à des bogues. Mais qu’est-ce que je veux dire par «bogue» ? Un bogue est un cas de test que vous n’avez pas encore écrit.
>>> import roman5 >>> roman5.fromRoman("") 0
Vous vous rappelez que dans la section précédente nous avons vu à chaque fois qu’une chaîne vide était reconnue par l’expression régulière que nous utilisons pour vérifier la validité des nombres romains. En fait, c’est toujours vrai pour la version finale de l’expression régulière. Et c’est un bogue, nous voulons qu’une chaîne vide déclenche une exception InvalidRomanNumeralError comme toute autre séquence de caractères qui ne représente pas un nombre romain valide. |
Après avoir reproduit le bogue et avant de le corriger, vous devez écrire un cas de test qui échoue, de manière à l’illustrer.
class FromRomanBadInput(unittest.TestCase): # previous test cases omitted for clarity (they haven't changed) def testBlank(self): """fromRoman should fail with blank string""" self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, "")
Puisque notre code a un bogue et que nous avons maintenant un cas de test pour ce bogue, le cas de test va échouer :
fromRoman should only accept uppercase input ... ok toRoman should always return uppercase ... ok fromRoman should fail with blank string ... FAIL fromRoman should fail with malformed antecedents ... ok fromRoman should fail with repeated pairs of numerals ... ok fromRoman should fail with too many repeated numerals ... ok fromRoman should give known result with known input ... ok toRoman should give known result with known input ... ok fromRoman(toRoman(n))==n for all n ... ok toRoman should fail with non-integer input ... ok toRoman should fail with negative input ... ok toRoman should fail with large input ... ok toRoman should fail with 0 input ... ok ====================================================================== FAIL: fromRoman should fail with blank string ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\docbook\dip\py\roman\stage6\romantest61.py", line 137, in testBlank self.assertRaises(roman61.InvalidRomanNumeralError, roman61.fromRoman, "") File "c:\python21\lib\unittest.py", line 266, in failUnlessRaises raise self.failureException, excName AssertionError: InvalidRomanNumeralError ---------------------------------------------------------------------- Ran 13 tests in 2.864s FAILED (failures=1)
Maintenant nous pouvons corriger le bogue.
Ce fichier est disponible dans le sous-répertoire py/roman/stage6/ du répertoire des exemples.
def fromRoman(s): """convert Roman numeral to integer""" if not s: raise InvalidRomanNumeralError, 'Input can not be blank' if not re.search(romanNumeralPattern, s): raise InvalidRomanNumeralError, 'Invalid Roman numeral: %s' % s result = 0 index = 0 for numeral, integer in romanNumeralMap: while s[index:index+len(numeral)] == numeral: result += integer index += len(numeral) return result
fromRoman should only accept uppercase input ... ok toRoman should always return uppercase ... ok fromRoman should fail with blank string ... ok fromRoman should fail with malformed antecedents ... ok fromRoman should fail with repeated pairs of numerals ... ok fromRoman should fail with too many repeated numerals ... ok fromRoman should give known result with known input ... ok toRoman should give known result with known input ... ok fromRoman(toRoman(n))==n for all n ... ok toRoman should fail with non-integer input ... ok toRoman should fail with negative input ... ok toRoman should fail with large input ... ok toRoman should fail with 0 input ... ok ---------------------------------------------------------------------- Ran 13 tests in 2.834s OK
Programmer de cette manière ne rend pas la correction de bogues plus simple. Les bogues simples (comme ici) nécessitent des cas de tests simples, les bogues complexes de cas de tests complexes. Dans un environnement centré sur les tests, il peut sembler que la correction d’un bogue prend plus de temps puisque vous devez définir exactement par du code ce qu’est le bogue (pour écrire le cas de test) avant de corriger le bogue proprement dit. Puis, si le cas de test ne passe pas immédiatement, vous devez déterminer si la correction est erronée ou si le cas de test a lui-même un bogue. Cependant, à terme, ces aller-retours entre le code de test et le code testé est rentable car il rend plus probable la correction des bogues du premier coup. De plus, puisque vous pouvez facilement lancer tous les cas de tests en même temps que le nouveau, vous êtes beaucoup moins susceptibles d’endommager une partie de l’ancien code en corrigeant le nouveau. Les tests unitaires d’aujourd’hui sont les tests de non régression de demain.
<< roman.py, étape 5 |
| 1 | 2 | 3 | 4 | 5 | |
Gestion des changements de spécification >> |