2011-03-01 4 views
10

estoy usando empezando a utilizar una pitón mock library para mi prueba. Quiero simular un módulo que se importa dentro del espacio de nombres del módulo bajo prueba sin realmente importarlo o exigir que exista primero (es decir, lanzar un ImportError).Python: Prototipo de un módulo sin importarlo o que necesitan que exista

Supongamos que existe el siguiente código:

foo.py

import helpers 
def foo_func(): 
    return helpers.helper_func() 

El objetivo es poner a prueba foo_func() cuando 'helpers.py' no existe en ninguna parte, y si existe, acto como si no fuera así

primer intento, mediante el super cool decorador @patch:

from mock import patch, sentinel 
import foo 
@patch("foo.helpers") 
def foo_test(mock): 
    mock.helper_func.return_value = sentinel.foobar 
    assert foo.foo_func() == sentinel.foobar 

Esto funciona si el módulo de "ayudantes" se puede importar. Si no existe, obtengo un ImportError.

siguiente intento con el parche, sans decorador:

from mock import patch, sentinel, Mock 
import foo 
helpers_mock = patch("foo.helpers") 
helpers_mock.start() 

def foo_test(): 
    helpers_mock.helper_func = Mock('helper_func') 
    helpers_mock.helper_func.return_value = sentinel.foobar 
    assert foo.foo_func() == sentinel.foobar 

Una vez más, esto no funciona si "ayudantes" que falta ... y, si existe, la afirmación falla. No estoy muy seguro de por qué sucede eso.

tercer intento, solución actual:

import sys 
helpers_mock = Mock(name="helpers_mock", spec=['helper_func']) 
helpers_mock.__name__ = 'helpers' 
sys.modules['helpers'] = helpers_mock 
import foo 
def foo_test(): 
    helpers_mock.helper_func.return_value = sentinel.foobar 
    assert foo.foo_func() == sentinel.foobar 

pasa esta prueba, independientemente de si existe o no "helpers.py".

¿Es esta la mejor manera de lograr este objetivo? ¿La biblioteca burlona que estoy usando ofrece una alternativa a esto? ¿De qué otra manera puedo hacer esto?

+0

Mientras yo estoy a favor de prueba unitaria Creo que en este caso está probando una característica del lenguaje y no una función útil de su código. También puede corregir su importación en foo.py para que parte de este parche de mono no sea necesario con un guardia de importación (por ejemplo: try: import helpers excepto ImportError: helpers = None en foo.py en lugar de la importación bare). Me refiero a que ahora mismo foo.py no está escrito en a para permitir que falten los ayudantes y permitir eso debería simplificar enormemente tus pruebas. – stderr

+0

Me gusta mucho la idea del protector de importación, resuelve muchos problemas. Si uso el protector de importación como sugiere, puedo usar el primer ejemplo que di (@patch decorator). ¡Gracias! –

Respuesta

3

que estés tipo de falta el punto de lo que una Falsa. Se supone que debes construirlos cuando quieras un objeto con una interfaz particular, independientemente de cómo se implemente.

Lo que está haciendo es tratar de volver a implementar sistema de módulos de Python, y además es un abuso bastante horrible de variables globales para arrancar.

En lugar de hacer un módulo foo, crea una clase Foo, y pasan en los ayudantes en el constructor.

class Foo(object): 
    def __init__(self, helpers): 
     self.helpers = helpers 

# then, instead of import foo: 
foo = Foo(mock_helpers) 

Incluso si los verdaderos "ayudantes" es en realidad va a ser un módulo, no hay ninguna razón por la que necesita estar jugando con sys.modules y su puesta en marcha a través de import en sus pruebas.

Y si foo tiene que ser un módulo, que también está bien, pero hacerlo de esta manera:

# foo.py 
class Foo(object): 
    pass # same code as before, plus foo_func 

try: 
    import whatever 
    _singleton = Foo(whatever) 
except ImportError: 
    _singleton = Foo(something_else) 

def foo_func(): 
    return _singleton.foo_func() 

grandes trozos del trabajo de la biblioteca estándar de esta manera. Es prácticamente el estándar para definir módulos tipo singleton.

+1

Esta es realmente una buena información, desafortunadamente significaría reescribir una gran cantidad de código, lo cual no es práctico en este momento. Pero para nuevos proyectos definitivamente probaré este enfoque. –

Cuestiones relacionadas