2013-08-12 25 views
33

Czy można sfałszować wartość zwracaną przez funkcję wywoływaną w ramach innej funkcji, którą próbuję przetestować? Chciałbym wyśmiewać metodę (która będzie wywoływana w wielu testowanych przeze mnie metodach), aby zwracać moje określone zmienne za każdym razem, gdy zostanie wywołana. Na przykład:Używanie makiety patch.object Pythona do zmiany wartości zwracanej metody wywołanej w innej metodzie

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

oraz w badanej jednostki, chciałbym użyć makiety, aby zmienić wartość zwracaną uses_some_other_method() tak, że każdy czas jest nazywany w Foo, zwróci to, co określono w @patch.object(...)

Odpowiedz

53

Można to zrobić na dwa sposoby; z patchem i patch.object

poprawka zakłada, że ​​nie są bezpośrednio importować obiekt ale że jest on używany przez obiekt testowanej jak w poniższym

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

Jeśli bezpośrednio importowania moduł do testowania, można użyć patch.object następująco:

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

W obu przypadkach some_fn będzie „un-wyśmiewany” po zakończeniu funkcji testowej.

Edit: W celu drwić wiele funkcji, po prostu dodaj więcej dekoratorów do funkcji i dodać argumenty do podjęcia w dodatkowych parametrów

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

Należy pamiętać, że im bliżej dekorator jest definicji funkcji, wcześniej znajduje się na liście parametrów.

+1

Dziękuję za wyjaśnienie różnicy między patchem a patch.object. –

+0

jak "patch.object" dwie funkcje w tej samej klasie? –

+0

@JacksonTale zaktualizował odpowiedź. – Silfheed

5

to można zrobić coś takiego:

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

#testing.py 
form mock import patch 

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

Oto źródło, które można przeczytać: Patching in the wrong place

4

Pozwól mi wyjaśnić, co mówisz: chcesz przetestować Foo w testamencie, który wywołuje metodę zewnętrzną uses_some_other_method. Zamiast wywoływać właściwą metodę, należy wyśmiać wartość zwracaną.

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

Załóżmy, ze ten kod jest w foo.py, uses_some_other_method definiuje moduł bar.py. Oto unittest:

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) 

Jeśli chcesz zmienić wartość zwracana za każdym razem, kiedy przeszedł w różnych arguements, mock zapewnia side_effect.