programing

클래스 조롱:모의() 또는 패치()?

closeapi 2023. 6. 5. 23:56
반응형

클래스 조롱:모의() 또는 패치()?

저는 Python과 함께 mock을 사용하고 있으며 이 두 가지 접근 방식 중 어느 것이 더 나은지 궁금합니다(읽기: 더 파이썬).

방법 1: 그냥 모의 물체를 만들고 그것을 사용합니다.코드는 다음과 같습니다.

def test_one (self):
    mock = Mock()
    mock.method.return_value = True 
    self.sut.something(mock) # This should called mock.method and checks the result. 
    self.assertTrue(mock.method.called)

방법 2:패치를 사용하여 모의를 만듭니다.코드는 다음과 같습니다.

@patch("MyClass")
def test_two (self, mock):
    instance = mock.return_value
    instance.method.return_value = True
    self.sut.something(instance) # This should called mock.method and checks the result. 
    self.assertTrue(instance.method.called)

두 방법 모두 동일한 작업을 수행합니다.저는 그 차이에 대해 확신이 없습니다.

누가 나를 깨우쳐 줄 수 있습니까?

mock.patch 와는 매우 다른 생물입니다.mock.Mock.patch 클래스를 모의 개체로 대체하고 모의 인스턴스로 작업할 수 있습니다.다음 스니펫을 보십시오.

>>> class MyClass(object):
...   def __init__(self):
...     print 'Created MyClass@{0}'.format(id(self))
... 
>>> def create_instance():
...   return MyClass()
... 
>>> x = create_instance()
Created MyClass@4299548304
>>> 
>>> @mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
...   MyClass.return_value = 'foo'
...   return create_instance()
... 
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
...   print MyClass
...   return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass@4300234128
<__main__.MyClass object at 0x100505d90>

patch대체MyClass호출하는 함수에서 클래스의 사용을 제어할 수 있는 방식으로.클래스에 패치를 적용하면 클래스에 대한 참조가 모의 인스턴스로 완전히 대체됩니다.

mock.patch일반적으로 테스트 내부에 클래스의 새 인스턴스를 만드는 무언가를 테스트할 때 사용됩니다. mock.Mock인스턴스가 더 명확하고 선호됩니다.만약 당신이self.sut.something메서드가 인스턴스를 생성했습니다.MyClass인스턴스를 매개 변수로 받는 대신,mock.patch여기가 적절할 것입니다.

저는 이것에 대한 유튜브 영상을 가지고 있습니다.

단답:사용하다mock당신이 조롱받고 싶은 것을 건네고 있을 때, 그리고.patch당신이 아니라면요둘 중에서 mock은 적절한 의존성 주입으로 코드를 작성하고 있다는 의미이기 때문에 강력하게 선호됩니다.

어리석은 예:

# Use a mock to test this.
my_custom_tweeter(twitter_api, sentence):
    sentence.replace('cks','x')   # We're cool and hip.
    twitter_api.send(sentence)

# Use a patch to mock out twitter_api. You have to patch the Twitter() module/class 
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_tweeter(sentence):
    twitter_api = Twitter(user="XXX", password="YYY")
    sentence.replace('cks','x') 
    twitter_api.send(sentence)

unittest.mock 작업 시 차이점을 설명하고 지침을 제공하는 핵심 사항

  1. 테스트 대상 객체의 일부 인터페이스 요소(인수 전달)를 바꾸려면 Mock 사용
  2. 테스트 대상 개체의 일부 개체 및 가져온 모듈에 대한 내부 호출을 바꾸려면 패치를 사용합니다.
  3. 항상 조롱하는 개체의 사양을 제공합니다.
    • 패치를 사용하면 항상 자동 사양을 제공할 수 있습니다.
    • Mock을 사용하면 사양을 제공할 수 있습니다.
    • Mock 대신 create_autospec을 사용할 수 있습니다. create_autospec은 규격을 사용하여 Mock 객체를 생성하기 위한 것입니다.

위의 질문에서 정답은 다음과 같습니다.Mock또는 더 정확히 말하면.create_autospec(당신이 조롱하는 클래스의 모의 방법에 스펙을 추가할 것이기 때문에), 정의된.spec존재하지 않는 클래스의 메서드를 호출하려는 경우(서명에 관계없이) 모의에서 도움이 될 것입니다. 몇 가지를 확인하십시오.

from unittest import TestCase
from unittest.mock import Mock, create_autospec, patch


class MyClass:
    
    @staticmethod
    def method(foo, bar):
        print(foo)


def something(some_class: MyClass):
    arg = 1
    # Would fail becuase of wrong parameters passed to methd.
    return some_class.method(arg)


def second(some_class: MyClass):
    arg = 1
    return some_class.unexisted_method(arg)


class TestSomethingTestCase(TestCase):
    def test_something_with_autospec(self):
        mock = create_autospec(MyClass)
        mock.method.return_value = True
        # Fails because of signature misuse.
        result = something(mock)
        self.assertTrue(result)
        self.assertTrue(mock.method.called)
    
    def test_something(self):
        mock = Mock()  # Note that Mock(spec=MyClass) will also pass, because signatures of mock don't have spec.
        mock.method.return_value = True
        
        result = something(mock)
        
        self.assertTrue(result)
        self.assertTrue(mock.method.called)
        
    def test_second_with_patch_autospec(self):
        with patch(f'{__name__}.MyClass', autospec=True) as mock:
            # Fails because of signature misuse.
            result = second(mock)
        self.assertTrue(result)
        self.assertTrue(mock.unexisted_method.called)


class TestSecondTestCase(TestCase):
    def test_second_with_autospec(self):
        mock = Mock(spec=MyClass)
        # Fails because of signature misuse.
        result = second(mock)
        self.assertTrue(result)
        self.assertTrue(mock.unexisted_method.called)
    
    def test_second_with_patch_autospec(self):
        with patch(f'{__name__}.MyClass', autospec=True) as mock:
            # Fails because of signature misuse.
            result = second(mock)
        self.assertTrue(result)
        self.assertTrue(mock.unexisted_method.called)
    
    def test_second(self):
        mock = Mock()
        mock.unexisted_method.return_value = True
        
        result = second(mock)
        
        self.assertTrue(result)
        self.assertTrue(mock.unexisted_method.called)

정의된 규격이 사용된 테스트 사례는 다음에서 메서드를 호출했기 때문에 실패합니다.something그리고.second함수는 MyClass에 불만이 없습니다. 즉, 버그를 잡는 반면 기본값입니다.Mock표시됩니다.

참고로 patch.object를 사용하여 호출되는 클래스 메서드만 조롱하는 옵션이 하나 더 있습니다.

클래스가 기능의 내부 부분으로 사용되는 경우 패치의 좋은 사용 사례는 다음과 같습니다.

def something():
    arg = 1
    return MyClass.method(arg)

그러면 패치를 장식품으로 사용하여 My Class를 조롱하고 싶을 것입니다.

언급URL : https://stackoverflow.com/questions/8180769/mocking-a-class-mock-or-patch

반응형