300 votes

Comment puis-je simuler une ouverture utilisée dans une instruction with (en utilisant le framework Mock en Python)?

Comment tester le code suivant avec des modèles (avec des modèles, le décorateur de patch et les sentinelles fournis par le cadre Mock de Michael Foord ):

 def testme(filepath):
    with open(filepath, 'r') as f:
        return f.read()
 

158voto

fuzzyman Points 3945

La façon de faire cela a changé dans la maquette 0.7.0 qui prend enfin en charge la réplication des méthodes du protocole python (méthodes magiques), en particulier à l'aide de MagicMock:

http://www.voidspace.org.uk/python/mock/magicmock.html

Un exemple de moquage ouvert en tant que gestionnaire de contexte (à partir de la page des exemples dans la documentation fictive):

 >>> open_name = '%s.open' % __name__
>>> with patch(open_name, create=True) as mock_open:
...     mock_open.return_value = MagicMock(spec=file)
...
...     with open('/some/path', 'w') as f:
...         f.write('something')
...
<mock.Mock object at 0x...>
>>> file_handle = mock_open.return_value.__enter__.return_value
>>> file_handle.write.assert_called_with('something')
 

86voto

David Points 3254

Avec les dernières versions de fantaisie, vous pouvez utiliser le vraiment utile mock_open helper:

mock_open(mock=None, read_data=None)

Une fonction d'aide pour créer un maquette de remplacer l'utilisation de l'open. Il fonctionne pour ouvrir appelé directement ou utilisé comme un gestionnaire de contexte.

La maquette argument est l'objet fantaisie à configurer. Si Aucun ( défaut), puis un MagicMock sera créé pour vous, avec l'API limitée à des méthodes ou des attributs disponibles sur le standard de descripteurs de fichiers.

read_data est une chaîne de caractères pour la méthode de lecture du descripteur de fichier à retour. C'est une chaîne vide par défaut.

>>> from mock import mock_open, patch
>>> m = mock_open()
>>> with patch('{}.open'.format(__name__), m, create=True):
...    with open('foo', 'w') as h:
...        h.write('some stuff')

>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

22voto

Casey Points 19286

Mise à jour de la réponse de Daryl pour corriger les modifications apportées à la classe Mock.

 @patch('__builtin__.open')
def test_testme(self, open_mock):
    #
    # setup
    #
    context_manager_mock = Mock()
    open_mock.return_value = context_manager_mock
    file_mock = Mock()
    file_mock.read.return_value = sentinel.file_contents
    enter_mock = Mock()
    enter_mock.return_value = file_mock
    exit_mock  = Mock()
    setattr( context_manager_mock, '__enter__', enter_mock )
    setattr( context_manager_mock, '__exit__', exit_mock )

    #
    # exercise
    #
    result = cbot.testme(sentinel.filepath)

    #
    # verify
    #
    self.assertEquals(result, sentinel.file_contents)
    self.assertEquals(open_mock.call_args,
                      ((sentinel.filepath, 'r'), {}))
    self.assertEquals(context_manager_mock.method_calls,
                      [('__enter__', (), {}),
                       ('__exit__', (None, None, None), {})])
    self.assertEquals(file_mock.method_calls, [('read', (), {})])
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X