¿Cómo me burlo de un open usado en una declaración with (usando el marco Mock en Python)?

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

  •  18-09-2019
  •  | 
  •  

Pregunta

¿Cómo pruebo el siguiente código con simulacros (usando simulacros, el decorador de parches y los centinelas proporcionados por Marco simulado de Michael Foord):

def testme(filepath):
    with open(filepath, 'r') as f:
        return f.read()
¿Fue útil?

Solución

La manera de hacer esto ha cambiado en mock 0.7.0 que finalmente soporta burlarse de los métodos de protocolo pitón (métodos mágicos), en particular mediante el MagicMock:

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

Un ejemplo de burlarse abierto como un gestor de contexto (desde la página de ejemplos en la documentación mock):

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

Otros consejos

Hay mucho ruido en estas respuestas;casi todos son correctos pero están desactualizados y no están ordenados. mock_open es parte de mock framework y es muy sencillo de utilizar. patch usado como contexto devuelve el objeto usado para reemplazar el parcheado:puedes usarlo para simplificar tu prueba.

Pitón 3.x

Usar builtins en lugar de __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")

Pitón 2.7

mock no es parte de unittest y deberías parchear __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")

Estuche decorador

si usaras patch como decorador usando mock_open()El resultado como new patchEl argumento puede ser un poco extraño.

En este caso es mejor utilizar el new_callable patchargumento y recuerda que cada argumento adicional que patch no utiliza se pasará a new_callable funcionar como se describe en patch documentación.

patch() toma argumentos de palabras clave arbitrarios.Estos se pasarán al Mock (o new_callable) en la construcción.

Por ejemplo, versión decorada para Pitón 3.x es:

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

Recuerda que en este caso patch Agregará el objeto simulado como argumento de su función de prueba.

Con las últimas versiones del simulacro, se puede utilizar el mock_open helper:

  

mock_open (mock = Ninguno, read_data = None)

     

Una función auxiliar para crear una   maqueta para reemplazar el uso de abierta. Funciona para abierto llamada directa o   utilizado como un gestor de contexto.

     

El argumento es el simulacro objeto de burla para configurar. Si Ninguno (el   predeterminado), entonces se creará un MagicMock para usted, con la API   limitado a los métodos o atributos disponibles en los mangos de archivo estándar.

     

read_data es una cadena para el método de lectura del identificador de archivo   regreso. Esta es una cadena vacía por defecto.

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

Para usar mock_open de un simple archivo read() (el original mock_open fragmento ya dada en esta página se orienta más para escribir):

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 según documentación para mock_open, esto es específicamente para read(), por lo que no funcionará con patrones comunes como for line in f, por ejemplo.

Utiliza Python 2.6.6 / 1.0.1 simulacro

Puede ser que sea un poco tarde al juego, pero esta trabajado para mí al llamar open en otro módulo sin tener que crear un nuevo archivo.

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

Por parchear la función open el interior del módulo __builtin__ a mi mock_open(), que puede burlarse de la escritura en un archivo sin crear uno.

Nota: Si está utilizando un módulo que utiliza Cython, o su programa depende de Cython de cualquier manera, tendrá que importar módulo de __builtin__ Cython nofollow incluyendo import __builtin__ en la parte superior de su archivo. Usted no será capaz de burlarse de la __builtin__ universal, si está utilizando Cython.

La respuesta más común es útil pero se expandió en él un poco.

Si desea establecer el valor de su objeto de archivo (el f en as f) sobre la base de los argumentos que se pasan a open() aquí es una manera de hacerlo:

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

Básicamente, open() devolverá un objeto y with llamará __enter__() en ese objeto.

Para burlarse correctamente, hay que burlarse open() para devolver un objeto de burla. Ese objeto de burla luego se burle la llamada __enter__() en él (MagicMock lo hará por nosotros) para devolver el objeto de datos simulados / archivo que queremos (de ahí mm.__enter__.return_value). Hacer esto con 2 burla de la forma anterior nos permite capturar los argumentos que se pasan a open() y pasarlos a nuestro método do_something_with_data.

pasé todo un archivo de maqueta como una cadena de open() y mi do_something_with_data veía así:

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

Esto transforma la cadena en una lista para que pueda hacer lo siguiente como lo haría con un archivo normal:

for line in file:
    #do action
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top