web-dev-qa-db-fra.com

Se moquer de deux fonctions avec patch pour un test unitaire

J'ai une fonction que je veux tester unitairement contient deux autres fonctions. Je ne sais pas comment puis-je me moquer des deux fonctions en même temps correctement en utilisant le patch. J'ai fourni un exemple de ce que je veux dire ci-dessous. Quand je lance nos tests, les tests réussissent mais je pense qu'il doit y avoir une façon plus propre de le faire et je ne comprends pas vraiment la pièce concernant f.close () ...

La structure du répertoire ressemble à ceci:

program/
  program/
    data.py
  tests/
    data_test.py

data.py:

import cPickle

def write_out(file_path, data):
    f = open(file_path, 'wb')
    cPickle.dump(data, f)
    f.close()

data_test.py:

from mock import MagicMock, patch

def test_write_out():
    path = '~/collection'
    mock_open = MagicMock()
    mock_pickle = MagicMock()
    f_mock = MagicMock()
    with patch('__builtin__.open', mock_open):
        f = mock_open.return_value
        f.method.return_value = path
        with patch('cPickle.dump', mock_pickle):
            write_out(path, 'data')
            mock_open.assert_called_once_with('~/collection', 'wb')
            f.close.assert_any_call()
            mock_pickle.assert_called_once_with('data', f)

Résultats:

$ nosetests
.
----------------------------------------------------------------------
Ran 1 test in 0.008s
OK
54
cnodell

Vous pouvez simplifier votre test en utilisant le patch décorateur et en les imbriquant ainsi (ce sont des objets MagicMock par défaut):

@patch('cPickle.dump')
@patch('__builtin__.open')
def test_write_out(mock_open, mock_pickle):
    path = '~/collection'
    f = mock_open.return_value
    f.method.return_value = path

    write_out(path, 'data')

    mock_open.assert_called_once_with('~/collection', 'wb')
    mock_pickle.assert_called_once_with('data', f)
    f.close.assert_any_call()

Les appels à une instance MagicMock renvoient une nouvelle instance MagicMock, afin que vous puissiez vérifier que la valeur renvoyée a été appelée comme tout autre objet simulé. Dans ce cas, f est un MagicMock nommé 'open()' (essayez d'imprimer f).

81
Matti John

En plus de la réponse @Matti John, vous pouvez également utiliser patch à l'intérieur de la fonction test_write_out:

from mock import MagicMock, patch

def test_write_out():
    path = '~/collection'
    with patch('__builtin__.open') as mock_open, \
            patch('cPickle.dump') as mock_pickle:

        f = mock_open.return_value
        ...
36
Alex Lisovoy

Voici un exemple simple sur la façon de tester l'augmentation de ConflictError dans create_collection fonction utilisant une maquette:

import os
from unittest import TestCase
from mock import patch
from ..program.data import ConflictError, create_collection


class TestCreateCollection(TestCase):
    def test_path_exists(self):
        with patch.object(os.path, 'exists') as mock_method:
            mock_method.return_value = True

            self.assertRaises(ConflictError, create_collection, 'test')

S'il vous plaît, voyez aussi mock docs et le génial de Michael Foord introduction à mock .

2
alecxe