web-dev-qa-db-fra.com

Utiliser le fichier patch.object de python pour changer la valeur de retour d'une méthode appelée dans une autre méthode

Est-il possible de simuler une valeur de retour d'une fonction appelée dans une autre fonction que je tente de tester? Je voudrais que la méthode fausse (qui sera appelée dans beaucoup de méthodes que je teste) renvoie mes variables spécifiées à chaque appel. Par exemple:

class Foo:
    def method_1():
       results = uses_some_other_method()
    def method_n():
       results = uses_some_other_method()

Dans le test unitaire, je voudrais utiliser mock pour modifier la valeur de retour de uses_some_other_method() de sorte que chaque fois qu'il est appelé dans Foo, il renvoie ce que j'ai défini dans @patch.object(...)

51
mdoc-2011

Vous pouvez le faire de deux manières. avec patch et avec patch.object

Le correctif suppose que vous n'importez pas directement l'objet, mais qu'il est utilisé par l'objet que vous testez, comme indiqué ci-dessous.

#foo.py
def some_fn():
    return 'some_fn'

class Foo(object):
    def method_1(self):
        return some_fn()

#bar.py
import foo
class Bar(object):
    def method_2(self):
        tmp = foo.Foo()
        return tmp.method_1()

#test_case_1.py
import bar
from mock import patch

@patch('foo.some_fn')
def test_bar(mock_some_fn):
    mock_some_fn.return_value = 'test-val-1'
    tmp = bar.Bar()
    assert tmp.method_2() == 'test-val-1'
    mock_some_fn.return_value = 'test-val-2'
    assert tmp.method_2() == 'test-val-2'

Si vous importez directement le module à tester, vous pouvez utiliser patch.object comme suit:

#test_case_2.py
import foo
from mock import patch

@patch.object(foo, 'some_fn')
def test_foo(test_some_fn):
    test_some_fn.return_value = 'test-val-1'
    tmp = foo.Foo()
    assert tmp.method_1() == 'test-val-1'
    test_some_fn.return_value = 'test-val-2'
    assert tmp.method_1() == 'test-val-2'

Dans les deux cas, some_fn sera "irréprochable" une fois la fonction de test terminée.

Edit: Afin de simuler plusieurs fonctions, il suffit d’ajouter des décorateurs à la fonction et d’ajouter des arguments pour intégrer les paramètres supplémentaires.

@patch.object(foo, 'some_fn')
@patch.object(foo, 'other_fn')
def test_foo(test_other_fn, test_some_fn):
    ...

Notez que plus le décorateur est proche de la définition de la fonction, plus tôt il se trouve dans la liste des paramètres.

80
Silfheed

Cela peut être fait avec quelque chose comme ça:

# foo.py
class Foo:
    def method_1():
        results = uses_some_other_method()


# testing.py
from mock import patch

@patch('Foo.uses_some_other_method', return_value="specific_value"):
def test_some_other_method(mock_some_other_method):
    foo = Foo()
    the_value = foo.method_1()
    assert the_value == "specific_value"

Voici une source que vous pouvez lire: Patching au mauvais endroit

13
chipz

Laissez-moi clarifier de quoi vous parlez: vous voulez tester Foo dans un cas de test, qui appelle une méthode externe uses_some_other_method. Au lieu d'appeler la méthode réelle, vous voulez vous moquer de la valeur de retour.

class Foo:
    def method_1():
       results = uses_some_other_method()
    def method_n():
       results = uses_some_other_method()

Ok, supposons que le code ci-dessus soit en foo.py, uses_some_other_method est défini dans le module bar.py. Voici le plus modeste:

import unitest
import mock

from foo import Foo


class TestFoo(unittest.TestCase):

    def setup(self):
        self.foo = Foo()

    @mock.patch('foo.uses_some_other_method')
    def test_method_1(self, mock_method):
        mock_method.return_value = 3
        self.foo.method_1(*args, **kwargs)

        mock_method.assert_called_with(*args, **kwargs)

Si vous voulez changer la valeur de retour chaque fois que vous passez dans différents arguments, mock fournit side_effect .

5
cizixs