Come posso deridere un open utilizzato in un'istruzione with (utilizzando il framework Mock in Python)?

StackOverflow https://stackoverflow.com/questions/1289894

  •  18-09-2019
  •  | 
  •  

Domanda

Come posso testare il seguente codice con i mock (usando i mock, il decoratore di patch e le sentinelle fornite da Il framework Mock di Michael Foord):

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

Soluzione

Il modo per farlo è cambiato in finto 0.7.0 che finalmente supporta beffardo i metodi di protocollo di pitone (metodi magici), in particolare utilizzando il MagicMock:

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

Un esempio di scherno aperta come un contesto manager (dalla pagina esempi nella documentazione finto):

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

Altri suggerimenti

Non c'è molto rumore in queste risposte; quasi tutti sono corretti ma obsolete e non pulito. mock_open fa parte di quadro mock ed è molto semplice da usare. patch utilizzato come contesto restituisce l'oggetto utilizzato per sostituire quello patchato :. si può utilizzare per rendere il test più semplice

Python 3.x

Usa builtins invece di __builtin__.

from unittest.mock import patch, mock_open
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

Python 2.7

mock non fa parte del unittest e si dovrebbe patchare __builtin__

from mock import patch, mock_open
with patch("__builtin__.open", mock_open(read_data="data")) as mock_file:
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

caso Decorator

Se si utilizza patch come decoratrice usando il risultato di mock_open() come argomento della new patch può essere un po 'strano.

In questo caso è meglio usare l'argomento del new_callable patch e ricordare che ogni argomenti extra che patch non usa verranno passati al new_callable funzione come descritto in documentazione patch .

  

patch () prende argomenti chiave arbitrari. Questi saranno passati al Mock (o new_callable) sulla costruzione.

Per esempio versione decorata per Python 3.x è:

@patch("builtins.open", new_callable=mock_open, read_data="data")
def test_patch(mock_file):
    assert open("path/to/open").read() == "data"
    mock_file.assert_called_with("path/to/open")

Ricordate che in questo caso patch aggiungerà l'oggetto fittizio come argomento di voi funzione di test.

Con le ultime versioni di finto, è possibile utilizzare il mock_open helper:

  

mock_open (finto = Nessuno, read_data = Nessuno)

     

Una funzione di supporto per creare una   finto per sostituire l'uso di open. Si lavora per aprire chiamato direttamente o   utilizzato come un contesto manager.

     

L'argomento finto è l'oggetto fittizio da configurare. Se None (il   default) poi un MagicMock verrà creato per voi, con l'API   limitata a metodi o attributi disponibili sulle maniglie di file standard.

     

read_data è una stringa per il metodo di lettura del handle di file per   ritorno. Questa è una stringa vuota di default.

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

Usare mock_open per un file semplice read() (lo snippet originale mock_open già riportato in questa pagina è più adatto alla scrittura):

my_text = "some text to return when read() is called on the file object"
mocked_open_function = mock.mock_open(read_data=my_text)

with mock.patch("__builtin__.open", mocked_open_function):
    with open("any_string") as f:
        print f.read()

Nota come da documentazione per mock_open, questo è specifico per read(), quindi non funzionerà con modelli comuni come for line in f, Per esempio.

Utilizza Python 2.6.6 / mock 1.0.1

Potrei essere un po 'tardi per il gioco, ma questo ha funzionato per me quando si chiama open in un altro modulo, senza dover creare un nuovo file.

test.py

import unittest
from mock import Mock, patch, mock_open
from MyObj import MyObj

class TestObj(unittest.TestCase):
    open_ = mock_open()
    with patch.object(__builtin__, "open", open_):
        ref = MyObj()
        ref.save("myfile.txt")
    assert open_.call_args_list == [call("myfile.txt", "wb")]

MyObj.py

class MyObj(object):
    def save(self, filename):
        with open(filename, "wb") as f:
            f.write("sample text")

Con l'applicazione di patch la funzione open all'interno del modulo __builtin__ al mio mock_open(), posso prendere in giro la scrittura in un file senza creare uno.

Nota: Se si utilizza un modulo che utilizza Cython, o il vostro programma dipende Cython in qualsiasi modo, è necessario importare modulo __builtin__ di Cython nofollow includendo import __builtin__ nella parte superiore del file. Non sarà in grado di prendere in giro il __builtin__ universale se si utilizza Cython.

La risposta superiore è utile, ma ho ampliato un po '.

Se si desidera impostare il valore del vostro oggetto file (il f in as f) sulla base degli argomenti passati al open() Ecco un modo per farlo:

def save_arg_return_data(*args, **kwargs):
    mm = MagicMock(spec=file)
    mm.__enter__.return_value = do_something_with_data(*args, **kwargs)
    return mm
m = MagicMock()
m.side_effect = save_arg_return_array_of_data

# if your open() call is in the file mymodule.animals 
# use mymodule.animals as name_of_called_file
open_name = '%s.open' % name_of_called_file

with patch(open_name, m, create=True):
    #do testing here

Fondamentalmente, open() restituirà un oggetto e with chiamerà __enter__() su quell'oggetto.

Per deridere correttamente, dobbiamo prendere in giro open() per restituire un oggetto fittizio. Questo oggetto fittizio dovrebbe quindi prendere in giro la chiamata __enter__() su di esso (MagicMock farà questo per noi) per restituire l'oggetto di dati finte / file che vogliamo (da qui mm.__enter__.return_value). Fare questo con 2 prende in giro il modo sopra di noi permette di catturare gli argomenti passati al open() e passarle al nostro metodo do_something_with_data.

ho passato un intero file finto come una stringa di open() e la mia do_something_with_data si presentava così:

def do_something_with_data(*args, **kwargs):
    return args[0].split("\n")

Questo trasforma la stringa in una lista in modo da poter eseguire le seguenti operazioni come si farebbe con un normale file:

for line in file:
    #do action
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top