2012-09-21 24 views
16

La libreria di derisione che utilizzo è ... mock.Come fingere funzioni annidate?

Mi sono imbattuto in questo problema di "funzioni fittizie annidate" quando ho provato a scrivere il test case per una funzione (codice legacy).

Questa funzione utilizzava una funzione annidata molto complessa con pesanti dipendenze su altri moduli.

Mi chiedo se è possibile simulare le funzioni annidate con mock.

risposta

2

Si sta tentando di sostituire una funzione nidificata con un oggetto fittizio? Se è così, è abbastanza semplice, non importa quanto sia complicata la funzione. Puoi usare un MagicMock per sostituire praticamente qualsiasi oggetto python.

Se è necessario simulare una funzione che restituisce qualcosa, è sufficiente impostare il parametro MagicMock. Sarebbe simile a questa:

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

Tuttavia, se si sta cercando di testare un altro pezzo di codice che chiama la funzione super_nested da qualche parte dentro, e si vuole prendere in giro fuori, è necessario utilizzare un patch . Nella biblioteca finto, sarà simile a questa:

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 

Qui, nulla nel blocco with che normalmente chiamare super_nested invece chiamerà il super_nested_mock e solo restituire il valore impostato ad esso.

C'è un po 'di sottigliezza in quello che è esattamente necessario inserire nella chiamata patch. Principalmente, vuoi applicare una patch all'oggetto mentre il modulo che stai testando lo vedrebbe. Vedi "where to patch" per ulteriori istruzioni.

+1

Questo non funziona perché la funzione nidificata esiste solo nella funzione che voglio testare. Quindi 'patch' non può localizzarlo e sostituirlo direttamente. – satoru

+0

Vedo, immagino di aver frainteso cosa stavi cercando di testare esattamente. Lascerò questo qui per i posteri. Buona fortuna però. – Wilduck

5

Un'opzione è di cambiare la tua funzione in modo che opzionalmente accetti la funzione da chiamare ad esempio se si dispone di:

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

modificarla in:

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 

Poi usi "reali" avranno la funzione di interno destro, e nei test è possibile fornire il proprio.

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 

Si potrebbe anche fare questo:

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 

In questo modo è solo di definire l'override:

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

ad esempio è necessario prendere in giro chiamate di funzione nidificate (funzioni concatenate) da Google API DRIVE

quindi è necessario applicare la patch attraverso funzioni: service_mock(), file(), inserire(), fino all'ultimo execute() Risposta:

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} 

schema principale: prima. return_value. Secondo. return_value. Terzo. return_value .last. valore_invio = rsp

+2

Questo ha salvato il mio weekend. Si noti che non è necessario concatenare gli attributi nidificati con un valore 'return_value'. – Antwan

Problemi correlati