Vous avez raison, vos tests ne doivent pas vérifier que le random
module fait bien son travail; un unittest ne devrait tester que la classe elle-même, pas comment elle interagit avec un autre code (qui devrait être testé séparément).
Il est bien sûr tout à fait possible que votre code utilise random.randint()
mal; ou vous appelez à la random.randrange(1, self._sides)
place et votre dé ne lance jamais la valeur la plus élevée, mais ce serait un type de bogue différent, pas celui que vous pourriez attraper avec un plus insignifiant. Dans ce cas, votre die
appareil fonctionne comme prévu, mais la conception elle-même était défectueuse.
Dans ce cas, j'utiliserais la simulation pour remplacer la randint()
fonction, et je vérifierais seulement qu'elle a été appelée correctement. Python 3.3 et supérieur est livré avec le unittest.mock
module pour gérer ce type de test, mais vous pouvez installer le mock
package externe sur les anciennes versions pour obtenir exactement les mêmes fonctionnalités
import unittest
try:
from unittest.mock import patch
except ImportError:
# < python 3.3
from mock import patch
@patch('random.randint', return_value=3)
class TestDice(unittest.TestCase):
def _make_one(self, *args, **kw):
from die import Die
return Die(*args, **kw)
def test_standard_size(self, mocked_randint):
die = self._make_one()
result = die.roll()
mocked_randint.assert_called_with(1, 6)
self.assertEqual(result, 3)
def test_custom_size(self, mocked_randint):
die = self._make_one(sides=42)
result = die.roll()
mocked_randint.assert_called_with(1, 42)
self.assertEqual(result, 3)
if __name__ == '__main__':
unittest.main()
Avec la moquerie, votre test est maintenant très simple; il n'y a que 2 cas, vraiment. Le cas par défaut pour un dé à 6 faces et le cas des côtés personnalisés.
Il existe d'autres façons de remplacer temporairement la randint()
fonction dans l'espace de noms global de Die
, mais le mock
module rend cela plus simple. Le @mock.patch
décorateur s'applique ici à toutes les méthodes de test dans le cas de test; chaque méthode de test reçoit un argument supplémentaire, la random.randint()
fonction simulée, afin que nous puissions tester par rapport à la maquette pour voir si elle a bien été appelée correctement. L' return_value
argument spécifie ce qui est retourné par la maquette lorsqu'elle est appelée, afin que nous puissions vérifier que la die.roll()
méthode nous a bien renvoyé le résultat «aléatoire».
J'ai utilisé une autre meilleure pratique Pitton unittesting ici: importer la classe sous test dans le cadre du test. La _make_one
méthode effectue le travail d'importation et d'instanciation dans un test , de sorte que le module de test sera toujours chargé même si vous avez fait une erreur de syntaxe ou une autre erreur qui empêchera le module d'origine d'importer.
De cette façon, si vous avez fait une erreur dans le code du module lui-même, les tests seront toujours exécutés; ils échoueront simplement, vous informant de l'erreur dans votre code.
Pour être clair, les tests ci-dessus sont extrêmement simplistes. Le but ici n'est pas de tester ce qui random.randint()
a été appelé avec les bons arguments, par exemple. Au lieu de cela, l'objectif est de tester que l'unité produit les bons résultats compte tenu de certaines entrées, où ces entrées incluent les résultats d'autres unités non testées. En se moquant de la random.randint()
méthode, vous pouvez prendre le contrôle d'une autre entrée de votre code.
Dans les tests du monde réel , le code réel de votre unité sous test va être plus complexe; la relation avec les entrées passées à l'API et comment les autres unités sont ensuite invoquées peut être encore intéressante, et la moquerie vous donnera accès à des résultats intermédiaires, et vous permettra de définir les valeurs de retour pour ces appels.
Par exemple, dans le code qui authentifie les utilisateurs par rapport à un service OAuth2 tiers (une interaction en plusieurs étapes), vous souhaitez tester que votre code transmet les bonnes données à ce service tiers et vous permet de simuler différentes réponses d'erreur qui Le service tiers reviendrait, vous permettant de simuler différents scénarios sans avoir à créer vous-même un serveur OAuth2 complet. Ici, il est important de tester que les informations d'une première réponse ont été gérées correctement et ont été transmises à un appel de deuxième étape, vous devez donc voir que le service simulé est appelé correctement.