2012-09-21 16 views
16

La biblioteca burlona que uso es ... mock.Cómo simular funciones anidadas?

Me encontré con este problema de "funciones anidadas falsas" cuando traté de escribir el caso de prueba para una función (código heredado).

Esta función utiliza una función anidada muy compleja con fuertes dependencias en otros módulos.

Me pregunto si es posible simular funciones anidadas con mock.

Respuesta

2

¿Está tratando de reemplazar una función anidada con un objeto simulado? Si es así, eso es bastante sencillo, sin importar cuán complicada sea la función. Puede usar un MagicMock para reemplazar prácticamente cualquier objeto Python.

Si necesita simular una función que devuelve algo, puede simplemente configurar el parámetro MagicMockreturn_value. Se vería algo como esto:

>>> super_nested_mock = mock.MagicMock() 
>>> super_nested_mock.return_value = 42 
>>> super_nested_mock() 
42 

Sin embargo, si usted está tratando de probar otra pieza de código que llama a su función super_nested en algún lugar dentro, y quieren burlarse hacia fuera, usted tendrá que utilizar un patch . En la biblioteca de simulacro, que se verá algo como esto:

with patch('super_nested') as super_nested_mock: 
    super_nested_mock.return_value = "A good value to test with" 
    assert my_function_that_calls_super_nested(5) == 20 

Aquí, nada en el bloque with que normalmente llamar super_nested vez llamará al super_nested_mock y simplemente devolver el valor que establezca para ello.

Hay algo de sutileza en lo que necesita exactamente para poner en la llamada de parche. Principalmente, quiere parchar el objeto ya que el módulo que está probando lo verá. Consulte "where to patch" para obtener más instrucciones.

+1

Esto no funcionará porque la función anidada solo existe en la función que deseo probar. Entonces 'parche' no puede localizarlo ni reemplazarlo directamente. – satoru

+0

Veo, supongo que entendí mal qué es exactamente lo que estabas tratando de probar. Voy a dejar esto aquí por el bien de la posteridad. Buena suerte sin embargo. – Wilduck

5

Una opción es cambiar su función para que opcionalmente acepte la función para llamar, por ejemplo si tiene:

def fn_to_test(): 
    def inner_fn(): 
    return 1 
    return inner_fn() + 3 

Cambiar a:

def fn_to_test(inner_fn = null) 
    def inner_fn_orig(): 
    return 1 
    if inner_fn==null: 
    inner_fn = inner_fn_orig 
    return fn() + 3 

Entonces usos "reales" tendrán la función interna derecha, y en sus pruebas le puede proporcionar el suyo propio.

fn_to_test() # calls the real inner function 
def my_inner_fn(): 
    return 3 
fn_to_test(inner_fn=my_inner_fn) # calls the new version 

También puede hacer esto:

def fn_to_test(): 
    def inner_fn_orign(): 
    return 1 
    inner_fn = inner_fn_orig 
    try: 
    inner_fn = fn_to_test.inner_fn 
    excecpt AttributeError: 
    pass 
    return inner_fn() + 3 

De esta manera se acaba de definir la anulación:

fn_to_test() # calls the real inner function 
def my_inner_fn(): 
    return 3 
fn_to_test.inner_fn = my_inner_fn 
fn_to_test() # calls the new version 
18

por ejemplo que necesita para burlarse de llamadas a funciones anidadas (funciones encadenadas) de Google DRIVE API

result = get_drive_service().files().insert(body='body', convert=True).execute() 

Así que hay que parchear a través de funciones: service_mock(), archivos(), insert(), hasta la última ejecución() Respuesta:

from mock import patch 
with patch('path.to.import.get_drive_service') as service_mock: 
    service_mock.return_value.files.return_value.insert.\ 
    return_value.execute.return_value = {'key': 'value', 'status': 200} 

esquema principal: primero. return_value .segundo. return_value .third. return_value .last. return_value = rsp

+2

Esto me salvó el fin de semana. Tenga en cuenta que los atributos anidados no necesitan estar encadenados con un 'return_value'. – Antwan

Cuestiones relacionadas