Les mock objectsLorsque vous utilisez le TDD (Test Driven
Development) pour vos projets Python basés sur un framework (comme Zope
par exemple), un temps non négligeable est passé à la conception de Fakes
pour rendre le test indépendant de ressources externes.
Les Fakes (c'est le terme que j'ai toujours employé, à tort ou à raison) sont ces classes ou fonctions vides qui permettent de simuler un composant. Par exemple dans le test d'un module qui appel un serveur IMAP, on peut concevoir un faux serveur IMAP qui simule un sous-ensemble des échanges possibles. Cette mise en place permet de tester que le module se comporte comme attendu, dans un environnement vraisemblable. Ces Fakes peuvent se faire à n'importe quel niveau et l'aspect dynamique de Python permet toute les manipulations. On peut par exemple modifier à la volée une classe d'un module de la bibliothèque standard, à l'insu du code appelé pendant les tests. Un vrai truc d'agent secret quand même... La contrepartie, c'est que le code de ces Fakes, il faut le taper.. concretement, on créé un fake vide, on lance le test, on rajoute la méthode appelée par le code quand le code la réclame, on relance, etc.. Certains fakes sont presque aussi gros que le code simulé ! De plus, les fakes ne fournissent pas de feedback sur les appels qui ont reçus. (ce que j'appel les reversed-test) La solution pour minimiser le code à taper, et pour enrichir les reversed-tests, c'est les objets Mock. Ces objets, en plus d'avoir un nom très hype ;), sont des mutants à mémoire. Ils acceptent tous les appels et les enregistrent. Le testeur peut ensuite vérifier quels appels ont étés émis, et faire des assertions dessus. Il peut également étendre les Mock lorsqu'il souhaite que le Fake renvoi des valeurs. Il existe une implémentation en Python, qui se trouve ici : http://python-mock.sourceforge.net/ Exemple tiré du site:
class PersistanceTestCase(unittest.TestCase):
def testPersistData(self):
#set up the mock objects
mockCursor = Mock()
mockDB = Mock( { "cursor" : mockCursor } )
#call the function to be tested
persistData(testData, mockDB)
#test the correct calls were made on the database
objects mockDB.mockCheckCall(0, 'cursor')
mockCursor.mockCheckCall(0,
'execute',
'...some SQL...',
someData)
mockCursor.mockCheckCall(1,
'execute',
'...more SQL...',
moreData)
mockDB.mockCheckCall(1, 'commit')
mockDB.mockCheckCall(2, 'close')
Dans cet exemple, on vérifie dans le mock que le code a bien appelé execute,
commit et close, dans cet ordre précis.A utiliser sans modération, donc. Pour les fans il y a même un blog dédié...
|
A propos
|