mock으로 읽기 전용 속성을 모의하는 방법은 무엇입니까?


92

mock으로 readonly 속성을 어떻게 조롱 합니까?

나는 시도했다 :

setattr(obj.__class__, 'property_to_be_mocked', mock.Mock())

그러나 문제는 그것이 클래스의 모든 인스턴스에 적용된다는 것입니다.

다른 생각이 있습니까? 나는 전체 객체를 조롱하는 것이 아니라이 특정 속성 만 모의하고 싶습니다.

답변:


166

더 나은 방법은 메서드를 직접 PropertyMock조롱하는 것보다 속성을로 조롱하는 __get__것입니다.

문서에 명시되어 있습니다 . 검색 unittest.mock.PropertyMock: 클래스에서 속성 또는 기타 설명 자로 사용하기위한 모의. 가져올 때 반환 값을 지정할 수 있도록 및 메서드를 PropertyMock제공합니다 .__get____set__

방법은 다음과 같습니다.

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

def test(unittest.TestCase):
    with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction:
        mock_last_transaction.return_value = Transaction()
        myclass = MyClass()
        print myclass.last_transaction
        mock_last_transaction.assert_called_once_with()

나는 @property. 이 답변은 다른 답변 (및 다른 많은 질문에 대한 다른 답변)이 그렇지 않은 경우 저에게 효과적이었습니다.
AlanSE

3
이것이 수행되어야하는 방법입니다. "허용 된"답변을 이동하는 방법이 있었으면 좋겠습니다
vitiral

4
컨텍스트 관리자 호출에 반환 값을 포함하는 것이 약간 더 깔끔하다는 것을 알았습니다.```with mock.patch ( 'MyClass.last_transaction', new_callable = PropertyMock, return_value = Transaction ()) : ...```
wodow

사실, 나는 받아 들여진 대답을 이것으로 옮겼습니다.
charlax

1
mock.patch.object를 사용하는 것도 좋습니다. 클래스 이름을 문자열로 쓸 필요가 없기 때문입니다 (예제에서는 문제가되지 않습니다). 패키지 이름을 변경하고 그렇지 않은 경우 감지 / 수정하기가 더 쉽습니다. 테스트 업데이트
케빈

41

사실, 대답은 (평소처럼) 문서 에 있었는데, 예제를 따랐을 때 클래스 대신 인스턴스에 패치를 적용하고 있었기 때문입니다.

방법은 다음과 같습니다.

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

테스트 스위트에서 :

def test():
    # Make sure you patch on MyClass, not on a MyClass instance, otherwise
    # you'll get an AttributeError, because mock is using settattr and
    # last_transaction is a readonly property so there's no setter.
    with mock.patch(MyClass, 'last_transaction') as mock_last_transaction:
        mock_last_transaction.__get__ = mock.Mock(return_value=Transaction())
        myclass = MyClass()
        print myclass.last_transaction

14
사람들은 다른 예를 사용해야합니다. mock.PropertyMock그것을하는 방법입니다!
바이러스 성

4
글을 쓰는 시점 PropertyMock에는 존재하지 않았습니다.
charlax

6

재정의하려는 속성이있는 개체가 모의 개체 인 경우을 사용할 필요가 없습니다 patch.

대신 모의 유형 에 대한 PropertyMock속성을 만든 다음 재정의 할 수 있습니다 . 예를 들어 속성을 재정 의하여 반환하려면 다음을 수행하십시오.mock_rows.pages(mock_page, mock_page,)

mock_page = mock.create_autospec(reader.ReadRowsPage)
# TODO: set up mock_page.
mock_pages = mock.PropertyMock(return_value=(mock_page, mock_page,))
type(mock_rows).pages = mock_pages

1
Bam, 내가 원했던 것 (속성을 가진 자동 사양 개체). 그리고 동료로부터 적은 없습니다 🙋♂️
마크 맥도날드

6

아마도 스타일의 문제이지만 테스트에서 데코레이터를 선호하는 경우 @jamescastlefield의 답변 을 다음과 같이 변경할 수 있습니다.

class MyClass:
    @property
    def last_transaction(self):
        # an expensive and complicated DB query here
        pass

class Test(unittest.TestCase):
    @mock.patch('MyClass.last_transaction', new_callable=PropertyMock)
    def test(self, mock_last_transaction):
        mock_last_transaction.return_value = Transaction()
        myclass = MyClass()
        print myclass.last_transaction
        mock_last_transaction.assert_called_once_with()

6

pytest와 함께 사용하는 경우 pytest-mock코드를 단순화하고 컨텍스트 관리자 (예 with: 다음과 같은 문)를 사용하지 않아도 됩니다.

def test_name(mocker): # mocker is a fixture included in pytest-mock
    mocked_property = mocker.patch(
        'MyClass.property_to_be_mocked',
        new_callable=mocker.PropertyMock,
        return_value='any desired value'
    )
    o = MyClass()

    print(o.property_to_be_mocked) # this will print: any desired value

    mocked_property.assert_called_once_with()

0

mocked 속성에 액세스했는지 여부를 테스트하고 싶지 않다면 간단히 예상되는 return_value.

with mock.patch(MyClass, 'last_transaction', Transaction()):
    ...

0

@property원본에 의존 하기 위해 모의 가 필요한 경우 __get__사용자 정의를 만들 수 있습니다.MockProperty

class PropertyMock(mock.Mock):

    def __get__(self, obj, obj_type=None):
        return self(obj, obj_type)

용법:

class A:

  @property
  def f(self):
    return 123


original_get = A.f.__get__

def new_get(self, obj_type=None):
  return f'mocked result: {original_get(self, obj_type)}'


with mock.patch('__main__.A.f', new_callable=PropertyMock) as mock_foo:
  mock_foo.side_effect = new_get
  print(A().f)  # mocked result: 123
  print(mock_foo.call_count)  # 1
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.