Pregunta ¿Cómo simular una función definida en un módulo de un paquete?


Tengo una estructura siguiente:

|-- dirBar
|   |-- __init__.py
|   |-- bar.py
|-- foo.py
`-- test.py

bar.py

def returnBar():
    return 'Bar'

foo.py

from dirBar.bar import returnBar

def printFoo():
    print returnBar()

test.py

from mock import Mock

from foo import printFoo
from dirBar import bar

bar.returnBar = Mock(return_value='Foo')

printFoo()

el resultado de python test.py es Bar.

Cómo burlarse del printBar para hacer que regrese Foo así que eso printFoo lo imprimirá?

EDITAR: Sin modificar ningún otro archivo que test.py


32
2017-12-20 12:50


origen


Respuestas:


Supongo que te vas a burlar de la función returnBar, te gustaría usar patch decorador:

from mock import patch

from foo import printFoo

@patch('foo.returnBar')
def test_printFoo(mockBar):
    mockBar.return_value = 'Foo'
    printFoo()

test_printFoo()

20
2017-12-20 13:01



Solo importa el bar módulo antes de la foo módulo y simulacro:

from mock import Mock

from dirBar import bar
bar.returnBar = Mock(return_value='Foo')

from foo import printFoo

printFoo()

Cuando está importando el returnBar en foo.py, está vinculando el valor del módulo a una variable llamada returnBar. Esta variable es local por lo que se pone en el cierre de la printFoo() función cuando foo se importa - y los valores en el cierre no se pueden actualizar por código desde fuera. Por lo tanto, debería tener el nuevo valor (es decir, la función de burla) antes de la importación de foo.

EDITAR: la solución anterior funciona pero no es robusta ya que depende de ordenar las importaciones. Eso no es muy ideal. Otra solución (que me ocurrió después de la primera) es importar el bar módulo en foo.py en lugar de solo importar el returnBar() función:

from dirBar import bar

def printFoo():
    print bar.returnBar()

Esto funcionará porque el returnBar() ahora se recupera directamente de la bar módulo en lugar del cierre. Entonces, si actualizo el módulo, se recuperará la nueva función.


24
2017-12-20 12:56



El mejor lugar que he encontrado para resolver problemas de burla es: http://alexmarandon.com/articles/python_mock_gotchas/ 

Debería burlarse de la función del módulo que está siendo importado por la clase bajo prueba.


4
2018-03-12 15:24



Otra forma de lidiar con esos casos es usar alguna inyección de dependencia.

Una manera fácil de hacerlo en Python es usar el mágico **kwargs :

foo.py

from dirBar.bar import returnBar

def printFoo(**kwargs):
    real_returnBar = kwargs.get("returnBar", returnBar)
    print real_returnBar()

test.py

from mock import Mock

from foo import printFoo
from dirBar import bar

mocked_returnBar = Mock(return_value='Foo')

printFoo(returnBar=mocked_returnBar)

esto conducirá a un código más comprobable (y aumentará la modularidad / reutilización).


-3
2017-12-20 13:03