Python simula più valori di ritorno


168

Sto usando pythons mock.patch e vorrei cambiare il valore di ritorno per ogni chiamata. Ecco l'avvertimento: la funzione da correggere non ha input, quindi non posso modificare il valore di ritorno in base all'input.

Ecco il mio codice per riferimento.

def get_boolean_response():
    response = io.prompt('y/n').lower()
    while response not in ('y', 'n', 'yes', 'no'):
        io.echo('Not a valid input. Try again'])
        response = io.prompt('y/n').lower()

    return response in ('y', 'yes')

Il mio codice di prova:

@mock.patch('io')
def test_get_boolean_response(self, mock_io):
    #setup
    mock_io.prompt.return_value = ['x','y']
    result = operations.get_boolean_response()

    #test
    self.assertTrue(result)
    self.assertEqual(mock_io.prompt.call_count, 2)

io.promptè solo una versione indipendente dalla piattaforma (python 2 e 3) di "input". Quindi alla fine sto cercando di deridere l'input degli utenti. Ho provato a utilizzare un elenco per il valore restituito, ma ciò non sembra funzionare.

Puoi vedere che se il valore restituito è qualcosa di non valido, otterrò solo un ciclo infinito qui. Quindi ho bisogno di un modo per modificare eventualmente il valore di ritorno, in modo che il mio test finisca effettivamente.

(un altro modo possibile per rispondere a questa domanda potrebbe essere quello di spiegare come avrei potuto imitare l'input dell'utente in un test unitario)


Non è un problema di questa domanda principalmente perché non ho la possibilità di variare gli input.

Uno dei commenti della risposta a questa domanda è sulla stessa linea, ma non è stata fornita alcuna risposta / commento.


3
response is not 'y' or 'n' or 'yes' or 'no'nel non fare quello che pensi che faccia. Vedi Come testare una variabile con più valori? e si dovrebbe non usare isper confrontare i valori di stringa, l'uso ==di confrontare i valori e non le identità degli oggetti.
Martijn Pieters

Fai attenzione anche qui. Sembra che tu stia cercando di usare isper confrontare i letterali delle stringhe. Non farlo. Il fatto che funzioni (a volte) è solo un dettaglio di implementazione in CPython. Inoltre, response is not 'y' or 'n' or 'yes' or 'no'probabilmente non sta facendo quello che pensi che sia ...
mgilson,

Risposte:


300

Puoi assegnare un iterabile a side_effect, e il mock restituirà il valore successivo nella sequenza ogni volta che viene chiamato:

>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.side_effect = ['foo', 'bar', 'baz']
>>> m()
'foo'
>>> m()
'bar'
>>> m()
'baz'

Citando la Mock()documentazione :

Se side_effect è un iterabile, ogni chiamata al mock restituirà il valore successivo dall'iterabile.

Per inciso, il test response is not 'y' or 'n' or 'yes' or 'no'sarà non funziona; stai chiedendo se l'espressione (response is not 'y')è vera o 'y'è vera (sempre il caso, una stringa non vuota è sempre vera), ecc. Le varie espressioni su entrambi i lati degli oroperatori sono indipendenti . Vedi Come testare una variabile con più valori?

Non si dovrebbe inoltre usare isper testare contro una stringa. L'interprete CPython può riutilizzare gli oggetti stringa in determinate circostanze , ma questo non è un comportamento su cui dovresti contare.

Come tale, utilizzare:

response not in ('y', 'n', 'yes', 'no')

anziché; questo utilizzerà test di uguaglianza ( ==) per determinare se fa responseriferimento a una stringa con lo stesso contenuto (valore).

Lo stesso vale per response == 'y' or 'yes'; usa response in ('y', 'yes')invece.


C'è un modo per farlo con lo standard mock? C'è un modo per usare la patch con MagicMock come sto facendo con la simulazione standard?
Nick Humrich,

@Humdinger: questa è una caratteristica della Mockclasse stardard .
Martijn Pieters

17
L'assegnazione di un elenco sembra funzionare solo con Python 3. Test con Python 2.7 invece devo usare un iteratore ( m.side_effect = iter(['foo', 'bar', 'baz'])).
utente686249

1
@ user686249: Posso davvero riprodurlo, perché speculare da un metodo produce un lambda(una funzione), non un MagicMock. Un oggetto funzione non può avere proprietà, quindi l' side_effectattributo deve essere iterabile. Tuttavia, non dovresti speculare su questo metodo. Uso migliore mock.patch.object(requests.Session, 'post'); ciò si traduce in un oggetto patcher che specula automaticamente sul metodo e supporta side_effectcorrettamente.
Martijn Pieters

3
@ JoeMjr2: quando l'iteratore è esaurito, StopIterationviene sollevato. Puoi usare qualsiasi iteratore, quindi puoi usarlo itertools.chain(['Foo'], itertools.repeat('Bar'))per produrre Foouna volta, quindi per sempre Bar.
Martijn Pieters
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.