Python renvoie l'objet MagicMock au lieu de return_value

86

J'ai un fichier python a.pyqui contient deux classes Aet 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()

Je voudrais unittest method_ben classe en me Bmoquant A. Voici le contenu du fichier testa.pyà cet effet:

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

Je m'attends à entrer Mocked Adans la sortie. Mais ce que j'obtiens c'est:

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

Où vais-je mal?

Mehdi Jafarnia Jahromi
la source
1
Lors du test, A()renvoie le return_valuefrom mock_A(un standard MagicMock, car vous n'avez rien spécifié d'autre), qui n'est pas une instance de la classe A. Vous devez définir cela return_valuecomme quelque chose qui a un fichier défini method_a.
jonrsharpe
3
mock_a.method_a.return_value = 'Mocked A' => mock_a (). method_a.return_value = 'Mocked A' devrait être meilleur :)
Ali SAID OMAR
@AliSAIDOMAR est précisément correct, c'est la valeur de retour de l'appel mock_aqui devrait avoir la méthode, pas mock_aelle-même.
jonrsharpe
1
@jonrsharpe. Merci pour votre explication. J'ai juste essayé. Les deux mock_a().method_a.return_value = 'Mocked A'et a mock_a.return_value.method_a.return_value = 'Mocked A'travaillé. Merci beaucoup pour vos commentaires. Pourriez-vous s'il vous plaît aller de l'avant et le mettre comme réponse?
Mehdi Jafarnia Jahromi
@MehdiJafarniaJahromi merci beaucoup!
Niakros

Réponses:

96

Lorsque vous @mock.patch('a.A'), vous remplacez la classe Adans le code testé par mock_a.

En B.method_bvous définissez alors a = A(), qui est maintenant a = mock_a()- c'est à dire aest le return_valuede mock_a. Comme vous n'avez pas spécifié cette valeur, c'est une valeur régulière MagicMock; cela n'est pas configuré non plus, vous obtenez donc la réponse par défaut (encore une autre MagicMock) lorsque vous appelez des méthodes dessus.

, Vous voulez plutôt que de configurer le return_valuedemock_a avoir la méthode appropriée, que vous pouvez faire comme soit:

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

ou, peut-être plus explicitement:

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

Votre code aurait fonctionné dans le cas a = A(attribuer la classe, ne pas créer d'instance), comme alors a.method_a()aurait déclenché votre méthode fictive.

Jonrsharpe
la source
4
Impressionnant. Tu as fait ma journée.
Vishal
Salut @jonrsharpe, j'utilise un dataframe pandas avec df.columns pour vérifier ma condition if . Il n'utilise pas les parenthèses (c'est-à-dire qu'il n'est pas appelable). Que dois-je faire pour renvoyer une liste dans ce cas. Merci!
imsrgadich
@imsrgadich avez-vous besoin d'une maquette pour cela? Créez simplement le dataframe approprié, traitez-le comme une valeur de test.
jonrsharpe
@jonrsharpe ouais, je pourrais le faire mais j'exécute également df.drop dans ma méthode appelée que je dois affirmer et que je ne renvoie pas le dataframe de la méthode appelée. Cela crée un problème. J'ai trouvé un moyen de le mock_data.configure_mock(columns='my_column')résoudre. Merci pour la réponse. (réf: bradmontgomery.net/blog/how-world-do-you-mock-name-attribute )
imsrgadich
Cela ne semble pas fonctionner lorsque vous simulez deux modèles SQLAlchemy dans le même test. Le premier fonctionne bien, mais le second renverra un MagicMock indépendamment de ce que vous définissez.
Juha Untinen