2013-04-02 12 views
31

Mam funkcję Chcę, aby test jednostki zawierał połączenia dwóch innych funkcji. Nie jestem pewien, w jaki sposób mogę sfałszować obie funkcje w tym samym czasie, poprawnie używając łaty. Podałem przykład tego, co mam na myśli poniżej. Kiedy przeprowadzam testy nosa, testy mijają, ale czuję, że musi być na to czystszy sposób i naprawdę nie rozumiem tego fragmentu dotyczącego f.close() ...Kpiny z dwóch funkcji z łatką do testu jednostkowego

Struktura katalogów wygląda następująco:

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) 

Wyniki:

$ nosetests 
. 
---------------------------------------------------------------------- 
Ran 1 test in 0.008s 
OK 
+1

Myślę, że moje pierwotne pytanie było jasne, więc czyściłem go. Mam nadzieję, że to ukaże to, czego szukam dokładniej! – cnodell

Odpowiedz

47

Można uprościć test za pomocą dekoratora plaster i zagnieżdżania ich jak tak (są MagicMock obiekty domyślnie):

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

apeluje do instancji MagicMock powrócić nową MagicMock instancji, dzięki czemu można sprawdzić zwrócona wartość została wywołana tak jak każdy inny wyszydzany obiekt. W tym przypadku f to MagicMock o nazwie 'open()' (spróbuj wydrukować f).

+2

W swojej sugestii wprowadzasz dwa parametry, po jednym dla każdego z nich. Jak Python wie, który jest który? Nie znalazłem odpowiedzi na to w dokumentach. – steps

+8

Dekoratory są stosowane od dołu do góry, a kolejność parametrów musi być taka sama. Zobacz tutaj: http://www.voidspace.org.uk/python/mock/patch.html?highlight=stack#nesting-patch-decorators –

+0

Tak, przeczytałem to, ale nie znalazłem wystarczająco wyraźnego. Dzięki! – steps

2

Oto prosty przykład, w jaki sposób przetestować podnoszenie ConflictError w create_collection funkcji za pomocą makiety:

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

Proszę również zobaczyć mock docs i Michael Foord niesamowite introduction to mock.

+0

Dziękuję, że nawet próbujesz mi pomóc. To mi pomaga, ale bardziej skupiłem się na tym, jak kpić z wielu funkcji za pomocą łaty. Niestety, moje pytanie nie wyjaśniało tego. Oczyściłem to pytanie teraz. – cnodell

21

Oprócz odpowiedzi @Matti John można również użyć patch wewnątrz funkcji 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 
     ... 
Powiązane problemy