2013-04-02 12 views
31

Ho una funzione che voglio test dell'unità contiene chiamate altre due funzioni. Non sono sicuro di come possa prendere in giro entrambe le funzioni allo stesso tempo usando correttamente la patch. Ho fornito un esempio di cosa intendo di seguito. Quando eseguo nosetests, i test passano ma sento che ci deve essere un modo più pulito per fare questo e in realtà non capisco il pezzo riguardante f.close() ...Mocking di due funzioni con patch per un test di unità

La struttura di directory è simile a questa:

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) 

Risultati:

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

Credo che la mia domanda iniziale era chiaro così ho ripulito. Spero che questo mostri quello che sto cercando più precisamente! – cnodell

risposta

47

È possibile semplificare il test utilizzando il decoratore di patch e di nidificazione in questo modo (sono MagicMock oggetti per impostazione predefinita):

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

Le chiamate a un'istanza MagicMock restituiscono una nuova MagicMock esempio, in modo da poter controllare che il valore restituito è stato chiamato proprio come qualsiasi altro oggetto deriso. In questo caso, f è un 'open()' (prova a stampare f).

+2

Nel tuo suggerimento, introduci due parametri, uno per ogni simulazione. Come sa Python quale è quale? Non ho trovato la risposta a questo nei documenti. – steps

+8

I decoratori sono applicati dal basso verso l'alto e l'ordine dei parametri deve corrispondere a questo. Vedi qui: http://www.voidspace.org.uk/python/mock/patch.html?highlight=stack#nesting-patch-decorators –

+0

Sì, ho letto questo, ma non lo trova abbastanza chiaro. Grazie! – steps

2

Ecco un semplice esempio su come testare alzando ConflictError in create_collection funzione utilizzando finto:

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

preghiamo di consultare anche mock docs e Michael Foord di impressionante introduction to mock.

+0

Grazie per aver provato a darmi una mano. Questo mi aiuta, ma ero più concentrato su come simulare più funzioni usando la patch. Sfortunatamente, la mia domanda non lo ha chiarito. Ho ripulito la domanda ora. – cnodell

21

Oltre alla risposta @Matti John è anche possibile utilizzare la funzione patchtest_write_out all'interno:

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 
     ... 
Problemi correlati