99 votes

Mocking de deux fonctions avec patch pour un test unitaire

J'ai une fonction que je veux soumettre à un test unitaire et qui appelle deux autres fonctions. Je ne suis pas sûr de pouvoir simuler les deux fonctions en même temps en utilisant patch. J'ai fourni un exemple de ce que je veux dire ci-dessous. Lorsque j'exécute nosetests, les tests passent mais je pense qu'il doit y avoir une façon plus propre de faire cela et je ne comprends pas vraiment le morceau 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

139voto

Matti John Points 2734

Vous pouvez simplifier votre test en utilisant le décorateur de patchs et en les imbriquant comme suit (ils sont MagicMock par défaut) :

from unittest.mock import patch

@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()

Appels à un MagicMock renvoie une nouvelle instance MagicMock de sorte que vous pouvez vérifier que la valeur renvoyée a été appelée comme n'importe quel autre objet simulé. Dans ce cas f est un MagicMock nommé 'open()' (essayez d'imprimer f ).

74voto

Alex Lisovoy Points 4807

En plus de la réponse de @Matti John, vous pouvez aussi utiliser patch fonction interne 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
        ...

2voto

alecxe Points 50783

Voici un exemple simple sur la façon de tester la levée de fonds. ConflictError en create_collection en utilisant mock :

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')

Veuillez également consulter docs factices et l'impressionnant Michael Foord introduction au simulateur .

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