Python zwraca obiekt MagicMock zamiast return_value

86

Mam plik Pythona, a.pyktóry zawiera dwie klasy Ai B.

class A(object):
    def method_a(self):
        return "Class A method a"

class B(object):
    def method_b(self):
        a = A()
        print a.method_a()

Chciałbym odpocząć method_bw klasie B, kpiąc A. Oto zawartość pliku testa.pydo tego celu:

import unittest
import mock
import a


class TestB(unittest.TestCase):

    @mock.patch('a.A')
    def test_method_b(self, mock_a):
        mock_a.method_a.return_value = 'Mocked A'
        b = a.B()
        b.method_b()


if __name__ == '__main__':
    unittest.main()

Spodziewam się, że wyjdę Mocked A. Ale otrzymuję:

<MagicMock name='A().method_a()' id='4326621392'>

Gdzie ja robię źle?

Mehdi Jafarnia Jahromi
źródło
1
Podczas testowania A()zwraca return_valuefrom mock_A(zwykły MagicMock, ponieważ nie określono niczego innego), który nie jest instancją klasy A. Musisz ustawić to return_valuejako coś, co ma zdefiniowane method_a.
jonrsharpe
3
mock_a.method_a.return_value = 'Mocked A' => mock_a (). method_a.return_value = 'Mocked A' powinno być lepsze :)
Ali powiedział OMAR
@AliSAIDOMAR jest dokładnie poprawne, to wartość zwracana przez wywołanie mock_apowinna mieć metodę, a nie mock_asiebie.
jonrsharpe
1
@jonrsharpe. Dziękuję za wyjaśnienie. Właśnie próbowałem. Obie mock_a().method_a.return_value = 'Mocked A'i mock_a.return_value.method_a.return_value = 'Mocked A'działały. Wielkie dzięki za komentarze. Czy mógłbyś kontynuować i podać to jako odpowiedź?
Mehdi Jafarnia Jahromi
@MehdiJafarniaJahromi wielkie dzięki!
Niakros

Odpowiedzi:

96

Kiedy @mock.patch('a.A')zamieniasz klasę Aw testowanym kodzie na mock_a.

W B.method_btobie więc ustaw a = A(), co jest teraz a = mock_a()- czyli ajest return_valuez mock_a. Ponieważ nie określiłeś tej wartości, jest to zwykła MagicMock; to też nie jest skonfigurowane, więc otrzymujesz domyślną odpowiedź (jeszcze inną MagicMock) podczas wywoływania metod na niej.

Zamiast tego, chcesz skonfigurować of mieć odpowiednią metodę, którą można zrobić jako:return_valuemock_a

mock_a().method_a.return_value = 'Mocked A' 
    # ^ note parentheses

lub, być może bardziej szczegółowo:

mock_a.return_value.method_a.return_value = 'Mocked A'

Twój kod zadziałałby w tym przypadku a = A(przypisując klasę, a nie tworząc instancję), ponieważ wtedy a.method_a()wywołałby twoją metodę pozorowaną.

jonrsharpe
źródło
4
Niesamowite. Uczyniłeś mój dzień.
Vishal
Cześć @jonrsharpe, używam ramki danych pandy z df.columns, aby sprawdzić mój warunek if . Nie używa nawiasów (tj. Nie jest wywoływalne). Co mam zrobić, aby w takim przypadku zwrócić listę. Dzięki!
imsrgadich
@imsrgadich czy potrzebujesz do tego makiety ? Po prostu zbuduj odpowiednią ramkę danych i potraktuj ją jako wartość testową.
jonrsharpe
@jonrsharpe tak, mógłbym to zrobić, ale wykonuję również df.drop w mojej wywołanej metodzie, którą muszę potwierdzić, a ponadto nie zwracam ramki danych z wywołanej metody. To stwarza problem. Znalazłem sposób, mock_data.configure_mock(columns='my_column')który go rozwiązuje. Ale dzięki za odpowiedź. (ref: bradmontgomery.net/blog/how-world-do-you-mock-name-attribute )
imsrgadich
Wydaje się, że to nie działa, gdy mockujesz dwa modele SQLAlchemy w tym samym teście. Pierwsza działa OK, ale druga zwróci MagicMock niezależnie od tego, co zdefiniujesz.
Juha Untinen