• Introduzione
  • unittest.,finto o mock
  • Decoratore
  • posizione della Risorsa
  • Finto return_value vs side_effect
  • Finto Chiamate Nidificate
  • Verificare Eccezioni
  • Eliminare lru_cache
  • Falsa a Livello di Modulo e/Variabili Globali
  • Finto Metodo di Istanza
  • Finto Metodo di Classe
  • Mock Intera Classe
  • Finto Chiamate Asincrone
  • Finto Tipi di Istanza
  • Finto builtin open funzione
  • Conclusione

Introduzione

Beffardo risorse quando la scrittura di test in Python può essere fonte di confusione se non avete dimestichezza con il fare queste cose., In questo post ho intenzione di coprire vari aspetti del codice beffardo, che si spera sia una risorsa utile per coloro che sono un po ‘ bloccati.

Nota: negli esempi di codice sto usando pytest, ma per la maggior parte non dovrebbe avere importanza.

unittest.mock o mock

Per ‘prendere in giro’ una risorsa avremo bisogno prima del modulo mock, e questo è il nostro primo ostacolo: di quale versione abbiamo bisogno? cioè ce ne sono due e sembrano entrambi ufficiali (mock e unittest.mock).,

Il modulomock è una libreria retrocompatibile che puoi scaricare da PyPy, dove asunittest.mock è la stessa cosa ma compatibile solo con la versione di Python che stai usando.,

in quasi tutti i casi si desidera importare in questo modo:

import unittest.mock as mock

Per ulteriori esempi, vedi questa guida di riferimento

Decoratore

Il modo più comune per deridere le risorse è quello di utilizzare un Pitone decoratore intorno alla vostra funzione di test:

@mock.patch("thing")def test_stuff(mock_thing): mock_thing.return_value = 123

In questo caso, di cosa stiamo patch (thing) può essere una variabile o una funzione.,

Quando lo fai dovrai passare un argomento alla tua funzione (puoi chiamarlo come vuoi †) che sarà un MagicMock.

Questo significa che se non fai nient’altro, le chiamate a thing(almeno nell’esempio precedente) comporteranno il ritorno del valore 123.

† la convenzione consiste nel nominare la variabile mock_<noun>.,

Se sei un beffardo di più le cose, quindi ti stack il finto decoratori ontop di ogni altro, e passare lungo per la funzione di test:

@mock.patch("third")@mock.patch("second")@mock.patch("first")def test_stuff(mock_first, mock_second, mock_third): ...

posizione della Risorsa

e ‘ importante sapere che, quando beffardo, è necessario specificare la posizione della risorsa di essere preso in giro, rilevanti per cui e ‘ importata., Questo è meglio spiegato con l’esempio…

Immaginare ho un modulo app.foo e all’interno del modulo di importazione di un’altra dipendenza, in questo modo:

from app.bar import thing

Si potrebbe pensare che quando si chiama mock.patch che si passa un riferimento alla risorsa come app.bar.thing. Ciò sarebbe rilevante solo se la risorsa veniva chiamata con quel percorso completo all’interno del modulo app.foo (ad esempio se app.foo chiamato app.bar.thing(...)).,

Se il percorso completo dello spazio dei nomi non è referenziato, che non è nell’esempio precedente (si noti che importiamo solo la risorsathing). Significa che abbiamo bisogno di specificare lo spazio dei nomi di riferimento per deridere come, dove e ‘ importata:

@mock.patch('app.foo.thing')

anche se thing esiste all’interno del app.bar specifichiamo app.foo.thing come app.foo è dove abbiamo importato per l’uso. Questo cattura la gente fuori tutto il tempo.,

Finto return_value vs side_effect

Se la funzione ha un try/except intorno ad esso, quindi è possibile utilizzare side_effect a causa della chiamata di funzione per attivare un’Eccezione come il valore restituito:

@mock.patch('app.aws.sdk.confirm_sign_up', side_effect=Exception('whoops'))

Nota: se si fosse usato return_value=Exception('whoops') poi il finto restituisce la rappresentazione in forma di stringa di Eccezione, piuttosto che sollevare eccezioni come side_effect fa.,metodo deriso oggetto è stato chiamato:

La ragione per cui questo può essere più complicato è dovuto a come un finto restituisce un nuovo mock quando l’accesso a una proprietà di un mock:

Il precedente codice di errore:

AssertionError: expected call not found.Expected: listen(8080)Actual: listen(123)

È necessario assicurarsi che si affermano il finto al momento giusto:

Verificare Eccezioni

Se si desidera verificare che qualche pezzo di codice genera un Exception tipo quando ne abbiamo bisogno possiamo mock risorse specifiche per lanciare un’eccezione e quindi utilizzare pytest.raises come contesto il gestore di tutto il chiamante del nostro codice di verifica.,

Possiamo catturare e fare asserzioni contro questo comportamento previsto prendendo in giro prima la risorsa che vogliamo lanciare un’eccezione e farla lanciare la nostra falsa eccezione usando il parametroside_effect.

Successivamente specifichiamo l’esatto tipo di eccezione che ci aspettiamo di generare utilizzandopytest.raises(T):

Nota: non commettere l’errore di inserire alcuna asserzione all’interno delwith context manager., Una volta che l’eccezione viene sollevata dalla funzione chiamata all’interno del with context manager, tutto il codice dopo di esso all’interno del blocco viene saltato.

Eliminare lru_cache

Se la funzione che si desidera testare la functools.lru_cache decoratore applicata, quindi, è necessario essere consapevoli di giro la risposta di tale funzione sarà memorizzato nella cache in un test e il risultato memorizzato nella cache verrà restituito quando viene chiamata la funzione di nuovo a testare qualche altro comportamento (e potrebbe confondere, quando vedi la risposta imprevista).,

Per risolvere questo problema è molto semplice perché lru_cache fornisce funzioni aggiuntive quando decoratoring le funzioni, fornisce:

  • cache_info
  • cache_clear

L’ultimo (cache_clear) è quello che si potrebbe chiamare. Questo è dimostrato di seguito:

Nota: debug questo non è sempre ovvio., Più tardi dimostrerò come prendere in giro la funzione open incorporata, e in quello scenario mi sono imbattuto in questo problema, perché sebbene non stessi prendendo in giro la funzione di livello superiore stessa (stavo prendendo in giro la chiamata a open all’interno), il contenuto del file aperto era ciò che è stato restituito

Mock Module Level/Global Variables

Con una variabile modulo è possibile impostare il valore direttamente o utilizzare mock.patch.,

Nell’esempio seguente abbiamo la variabile client_id che è una variabile globale all’interno del app.aws modulo che ci importa di riferimento altrove nel nostro codice:

Nel mock.patch esempio, ci sono due cose da notare:

  1. non usare return_value.
  2. non esiste un’istanza finta passata alla funzione di test.,

Questo perché stiamo modificando una variabile e non una funzione diretta o “callable”, quindi non è necessario passare una simulazione nella funzione di test (se si desidera modificare il valore alcune volte all’interno del test stesso, si deriderebbe la variabile ma non assegnare immediatamente un valore nel decoratore).

Mock Instance Method

Esistono diversi modi per ottenere il mocking di un metodo di istanza., Un approccio comune è quello di utilizzare mock.patch.object, in questo modo:

un Altro approccio è quello di deridere il metodo come una normale funzione, ma si fa riferimento al metodo tramite il classname:

un Altro (anche se più pesante) approccio per beffardo di un metodo d’istanza di classe è quello di approfittare del fatto che un Finto restituisce un nuovo mock esempio quando chiamato:

@mock.patch("foo.bar.SomeClass")def test_stuff(mock_class): mock_class.return_value.made_up_function.return_value = "123"

Nota: nell’esempio sopra si è finto l’intera classe, che potrebbe non essere ciò che si desidera. In caso contrario, utilizzare l’esempio precedente mock.patch.object.,

Il motivo per cui l’esempio sopra funziona è perché stiamo impostandoreturn_value sul nostro mock. Perché questo è un MagicMock ogni attributo di riferimento restituisce un nuovo mock istanza di una funzione o di una proprietà chiamata su un mock non deve esistere), e così noi chiamiamo made_up_function restituito finto, e che appena creato mock abbiamo impostato il finale return_value 123.,

Ma come menzionato nella nota sopra, questo approccio potrebbe essere un po ‘ troppo schietto a seconda di quali sono le tue esigenze (se ti interessa se hai una classe di funzionamento o meno).

Mock Class Method

Prendere in giro un metodo di classe è un approccio simile a prendere in giro un metodo di istanza.,

Un approccio potrebbe essere quello di deridere l’intera classe (ma ora si dispone di uno di meno return_value per assegnare):

mock_class.ClassMethodName.return_value = "123"

O, meglio ancora, si dovrebbe finto come si farebbe con qualsiasi normale funzione, ma solo di riferimento, il metodo tramite la classe:

Finto Intera Classe

Per farsi beffe di un’intera classe è necessario impostare il return_value per essere una nuova istanza della classe.,

Vedi qui altri suggerimenti di beffa relativi alla classe

Chiamate asincrone simulate

Il codice asincrono beffardo è probabilmente l’aspetto più confuso del beffardo. La mia soluzione “vai a” ti spiegherò prima, ma dopo condividerò alcuni metodi alternativi che ho visto e provato in passato.,

Prima di considerare questo codice asincrono all’interno di un app.foo modulo:

import app.stuffasync def do_thing(x): return await app.stuff.some_concurrent_function(x)

Se abbiamo bisogno di prendere in giro la coroutine app.stuff.some_concurrent_function, quindi siamo in grado di risolvere il problema creando una funzione che agisce come una coroutine e permettono di essere configurabile per diversi tipi di risposte:

Nota: l’esempio utilizza tornado per l’esecuzione di un test asincrono.,e le alternative…

AsyncMock

Nota: questo utilizza il pacchetto pytest-asyncio per aiutare con i test asyncio codice

iniziamo con il codice di essere preso in giro…

import asyncioasync def sum(x, y): await asyncio.sleep(1) return x + y

Ora ecco come avevamo finto di…

Scimmia Patch

MagicMock Sottoclasse

Async Funzione Inline

Finto Istanza Tipi

Quando beffardo di un oggetto vi accorgerete che il finto sostituisce l’intero oggetto e quindi può causare superare delle prove (o negativo) in modi inaspettati.,

Significato, se avete bisogno di fare un mock più come il calcestruzzo interfaccia, quindi ci sono due modi per farlo:

  1. spec
  2. wrap

E ‘ possibile utilizzare mock spec funzione di imitare tutti i metodi/attributi dell’oggetto preso in giro. Ciò garantisce che i tuoi mock abbiano la stessa api degli oggetti che stanno sostituendo.

Nota: esiste un spec_setpiù rigoroso che genererà un AttributeError.,

Questo è meglio dimostrato con un esempio:

Ilwrap parametro d’altra parte permette di ‘spiare’ l’implementazione, così come influenzare il suo comportamento.,su che prende in giro l’intero oggetto, non solo un singolo metodo:

Finto incorporato aperto funzione

Python mock biblioteca fornisce un’astrazione per il beffardo builtin open funzione molto più semplice…

create=True param impostato su mock.patch significa che il mock.MagicMock restituita verrà automaticamente creare attributi che vengono chiamate in finto (questo perché il open funzione tenta di accedere a un sacco di cose diverse ed è più facile per finto per finto, tutti per voi).,

Conclusione

Lì finiremo. Speriamo che questo elenco di tecniche di beffa sarà in grado di vedere attraverso anche il più complesso di codice che devono essere testati. Fammi sapere cosa ne pensi su Twitter.