2014-09-01 2 views
23

실제로 액션을 제거하지 않고 테스트 케이스에 assert_call* 헬퍼를 가져올 수 있도록 객체를 패치하는 명확한 방법이 있습니까? 나는 다음과 같은 테스트를 통과 얻을 수있는 @patch 라인을 수정하는 방법을 예를 들어파이썬 모의 - 구현을 방해하지 않고 메소드 패칭

:

from unittest import TestCase 
from mock import patch 


class Potato(object): 
    def foo(self, n): 
     return self.bar(n) 

    def bar(self, n): 
     return n + 2 


class PotatoTest(TestCase): 

    @patch.object(Potato, 'foo') 
    def test_something(self, mock): 
     spud = Potato() 
     forty_two = spud.foo(n=40) 
     mock.assert_called_once_with(n=40) 
     self.assertEqual(forty_two, 42) 

아마 해킹 수를이 함께 side_effect를 사용하여,하지만 난 거기에 기대했다가 작동 더 좋은 방법이 될 것입니다 등의 기능 classmethods, staticmethods, 언 바운드 방법 모두에 같은 방식으로,

+0

'foo'와'bar'가 올바르게 정의되지 않았습니다. 그들은'def foo (self, n)'과'def bar (self, n)'이어야합니다. – chepner

+0

예, 고마워 ... 고정 – wim

+0

또한,'foo'는 호출 된 것이기 때문에 테스트 자체가 호출되고 있기 때문에 테스트중인 다른 코드보다는 호출 할 것이 많습니다. 마찬가지로,'forty_two'가 테스트 된 코드가 아닌 여러분의 * test *에 의해 특정 값으로 설정되는 것을 테스트하는 것은별로 가치가없는 것처럼 보입니다. – chepner

답변

15

유사 당신과 솔루션 만 사용 wraps :

def test_something(self): 
    spud = Potato() 
    with patch.object(Potato, 'foo', wraps=spud.foo) as mock: 
     forty_two = spud.foo(n=40) 
     mock.assert_called_once_with(n=40) 
    self.assertEqual(forty_two, 42) 
the documentation에 따르면

: mock 객체를 포장하기위한 항목 :

랩합니다. 랩이 None이 아니면 Mock을 호출하면 랩된 객체 (실제 결과 반환)로 호출이 전달됩니다. 모의 객체에 대한 속성 액세스는 객체의 해당 속성을 래핑하는 Mock 객체를 반환합니다 (존재하지 않는 속성에 액세스하려고 시도하면 이 AttributeError를 발생시킵니다).


class Potato(object): 

    def spam(self, n): 
     return self.foo(n=n) 

    def foo(self, n): 
     return self.bar(n) 

    def bar(self, n): 
     return n + 2 


class PotatoTest(TestCase): 

    def test_something(self): 
     spud = Potato() 
     with patch.object(Potato, 'foo', wraps=spud.foo) as mock: 
      forty_two = spud.spam(n=40) 
      mock.assert_called_once_with(n=40) 
     self.assertEqual(forty_two, 42) 
+0

감사합니다. 이것은 내 것보다 약간 좋네요 .. 컨텍스트 관리자 사용법 대신에 꾸미기 사용법에 어떤 방법이 있는지 알고 있습니까? – wim

+0

@wim,이 뜻인가요? http://pastebin.com/pNyWRBNq – falsetru

+1

아니요, 데코레이터에 새로운 Potato 인스턴스를 생성하면 실제로 테스트중인 객체의 상태를 잃어 버리기 때문에 바운드 메소드가 필요합니다. – wim

3

사용자 Quuxplusone에서 현상금에 언급 된 추가 요구 사항이 응답 주소 :

내 사용 사례에 대한 중요한 것은 그것이 @patch.mock 작업이다

는 것을 즉, 내가 Potato (이 예에서는 spud) 인스턴스와 spud.foo이라는 내 인스턴스 사이에 코드를 삽입 할 필요가 없습니다. spud이 생성 된 곳을 제어하지 않기 때문에 foo 메소드를 기웃 거리며 만들어 낸 spud이 필요합니다.

위에 설명 된 사용 사례

는 장식을 사용하여 너무 많은 문제없이 달성 될 수있다 : 대체 방법은 테스트중인 수정 가변 인수를 받아들이는 경우

import unittest 
import unittest.mock # Python 3 

def spy_decorator(method_to_decorate): 
    mock = unittest.mock.MagicMock() 
    def wrapper(self, *args, **kwargs): 
     mock(*args, **kwargs) 
     return method_to_decorate(self, *args, **kwargs) 
    wrapper.mock = mock 
    return wrapper 

def spam(n=42): 
    spud = Potato() 
    return spud.foo(n=n) 

class Potato(object): 

    def foo(self, n): 
     return self.bar(n) 

    def bar(self, n): 
     return n + 2 

class PotatoTest(unittest.TestCase): 

    def test_something(self): 
     foo = spy_decorator(Potato.foo) 
     with unittest.mock.patch.object(Potato, 'foo', foo): 
      forty_two = spam(n=40) 
     foo.mock.assert_called_once_with(n=40) 
     self.assertEqual(forty_two, 42) 


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

을, 당신은 CopyingMock를 초기화 할 수 있습니다 spy_decorator 안에 MagicMock 대신.

관련 문제