Pytanie Python symuluje wiele wartości zwracanych


Używam pythons mock.patch i chciałbym zmienić wartość zwracaną dla każdego połączenia. Oto zastrzeżenie: łatana funkcja nie ma żadnych danych wejściowych, więc nie mogę zmienić wartości zwracanej na podstawie danych wejściowych.

Oto mój kod dla odniesienia.

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')

Mój kod testowy:

@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 jest niezależną od platformy (python 2 i 3) wersją "input". Tak więc ostatecznie próbuję wyśmiać wkład użytkowników. Próbowałem użyć listy dla wartości zwracanej, ale to nie szew do pracy.

Możesz zobaczyć, że jeśli zwracana wartość jest czymś nieważnym, dostanę tutaj nieskończoną pętlę. Potrzebuję więc sposobu, aby ostatecznie zmienić wartość zwracaną, aby mój test faktycznie się zakończył.

(innym możliwym sposobem udzielenia odpowiedzi na to pytanie może być wyjaśnienie, w jaki sposób mogę naśladować dane wprowadzone przez użytkownika w teście jednostek)


Nie dupę to pytanie głównie dlatego, że nie mam możliwości różnicowania danych wejściowych.

Jeden z komentarzy odpowiedzi na to pytanie jest wzdłuż tych samych linii, ale nie podano odpowiedzi / komentarza.


76
2017-07-22 20:25


pochodzenie


response is not 'y' or 'n' or 'yes' or 'no' w nie robienie tego, co myślisz, że robi. Widzieć Jak przetestować jedną zmienną na wiele wartości? i powinieneś nie posługiwać się is porównać wartości łańcuchowe, użyj == porównywać wartości, a nie tożsamości obiektów. - Martijn Pieters♦
Uważaj też tutaj. Wygląda na to, że próbujesz go użyć is porównać ciągi literałów. Nie rób tego. Fakt, że to działa (czasami) jest tylko szczegółem implementacji w CPython. Również, response is not 'y' or 'n' or 'yes' or 'no' prawdopodobnie nie robi to, co myślisz, że jest ... - mgilson


Odpowiedzi:


Możesz przypisać iterable do side_effect, a faza zwróci następną wartość w sekwencji za każdym razem, gdy zostanie wywołana:

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

Cytując Mock() dokumentacja:

Gdyby efekt uboczny jest iterowalne, a następnie każde wywołanie makiety zwróci następną wartość z iteracji.

Na marginesie, test response is not 'y' or 'n' or 'yes' or 'no' będzie nie praca; pytasz, czy wyrażenie (response is not 'y') jest prawdą, lub 'y' jest prawdą (zawsze tak jest, niepusty łańcuch jest zawsze prawdziwy), itp. Różne wyrażenia po obu stronach or operatorzy są niezależny. Widzieć Jak przetestować jedną zmienną na wiele wartości?

Powinieneś również nie używać is przetestować na łańcuchu. Interpreter CPython może ponownie wykorzystaj obiekty typu string w pewnych okolicznościach, ale to nie jest zachowanie, na które powinieneś liczyć.

Jako takie, użyj:

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

zamiast; to użyje równość testy (==), aby określić, czy response odwołuje się do łańcucha o tej samej zawartości (wartości).

To samo dotyczy response == 'y' or 'yes'; posługiwać się response in ('y', 'yes') zamiast.


149
2017-07-22 20:34



Czy istnieje sposób, aby to zrobić ze standardem mock? Czy istnieje sposób na użycie łatki z MagicMock, tak jak robię to ze standardową próbą? - Nick Humrich
@ Humdinger: Jest to cecha charakterystyczna stardarda Mock klasa. - Martijn Pieters♦
@Humdinger: zauważ jednak, że mock.patch() domyślnie używa MagicMock, podklasa Mock. - Martijn Pieters♦
Przypisanie listy wydaje się działać tylko w pythonie 3. Testowanie w Pythonie 2.7 Potrzebuję zamiast tego używać iteratora (m.side_effect = iter(['foo', 'bar', 'baz'])). - user686249
@ user686249: Rzeczywiście mogę to powtórzyć, ponieważ speccing z metody powoduje lambda (funkcja), nie a MagicMock. Obiekt funkcji nie może mieć właściwości, więc side_effect atrybut ma być iterable. Nie powinieneś jednak wymyślać takiej metody. Lepsze wykorzystanie mock.patch.object(requests.Session, 'post'); w wyniku czego obiekt patchera automatycznie dostosowuje się do metody, i obsługuje side_effect prawidłowo. - Martijn Pieters♦