"unittest.mock" --- 시작하기
****************************

버전 3.3에 추가.


모의 객체 사용하기
==================


메서드를 패치하는 모의 객체
---------------------------

"Mock" 객체의 일반적인 용도는 다음과 같습니다:

* 메서드 패치하기

* 객체에 대한 메서드 호출 기록하기

객체의 메서드를 대체하여 시스템의 다른 부분에서 올바른 인자로 호출되었
는지 확인할 수 있습니다:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')
<MagicMock name='method()' id='...'>

일단 모의 객체가 사용되면 (이 예제에서는 "real.method") 사용 방법에 대
한 어서션을 만들 수 있도록 하는 메서드와 어트리뷰트를 제공합니다.

참고:

  이 예의 대부분에서 "Mock"과 "MagicMock" 클래스는 교환할 수 있습니다.
  "MagicMock"이 더 유능한 클래스이기 때문에 기본 사용하기에 적합합니다
  .

일단 모의 객체가 호출되면 그것의 "called" 어트리뷰트가 "True"로 설정됩
니다. 더 중요하게 "assert_called_with()"나 "assert_called_once_with()"
메서드를 사용하여 올바른 인자로 호출되었는지 확인할 수 있습니다.

이 예제는 "ProductionClass().method"를 호출하면 "something" 메서드가
호출되는지 테스트합니다:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)


객체의 메서드 호출을 위한 모의 객체
-----------------------------------

마지막 예제에서 우리는 객체에 메서드를 직접 패치하여 올바르게 호출되었
는지 확인했습니다. 또 다른 일반적인 사용 사례는 객체를 메서드(또는 테
스트 중인 시스템의 일부)에 전달한 다음 올바른 방식으로 사용되는지 확인
하는 것입니다.

아래의 간단한 "ProductionClass"에는 "closer" 메서드가 있습니다. 객체로
호출되면 그것의 "close"를 호출합니다.

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...

따라서 테스트하려면 "close" 메서드를 가진 객체를 전달하고 올바르게 호
출되었는지 확인해야 합니다.

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()

모의 객체에 'close' 메서드를 제공하기 위해 어떤 작업도 수행할 필요가
없습니다. close에 액세스하면 만들어집니다. 따라서, 'close'가 아직 호출
되지 않았다면 테스트에서 액세스할 때 만들어지지만,
"assert_called_with()"는 실패 예외를 발생시킵니다.


클래스 모킹하기
---------------

일반적인 사용 사례는 테스트 중인 코드가 인스턴스 화하는 클래스를 모킹
하는 것입니다. 클래스를 패치하면, 해당 클래스가 모의 객체로 바뀝니다.
인스턴스는 *클래스를 호출해서* 만들어집니다. 이는 모킹된 클래스의 반환
값을 확인하여 "모의 인스턴스"에 액세스한다는 것을 뜻합니다.

아래 예제에는 "Foo"를 인스턴스 화하고 그것의 메서드를 호출하는
"some_function" 함수가 있습니다. "patch()"에 대한 호출은 클래스 "Foo"
를 모의 객체로 대체합니다. "Foo" 인스턴스는 모의 객체를 호출한 결과라
서, 모의 객체 "return_value"를 수정하여 구성됩니다.

   >>> def some_function():
   ...     instance = module.Foo()
   ...     return instance.method()
   ...
   >>> with patch('module.Foo') as mock:
   ...     instance = mock.return_value
   ...     instance.method.return_value = 'the result'
   ...     result = some_function()
   ...     assert result == 'the result'


모의 객체 이름 붙이기
---------------------

모의 객체에 이름을 지정하는 것이 유용할 수 있습니다. 이름은 모의 객체
의 repr에 표시되며 모의 객체가 테스트 실패 메시지에 나타날 때 유용할
수 있습니다. 이 이름은 모의 객체의 어트리뷰트나 메서드에도 전파됩니다:

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>


모든 호출 추적하기
------------------

메서드에 대한 단일 호출 이상을 추적하려는 경우가 종종 있습니다.
"mock_calls" 어트리뷰트는 모의 객체의 자식 어트리뷰트에 대한 모든 호출
을 기록합니다 - 그리고 그들의 자식에 대해서도 마찬가지입니다.

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]

"mock_calls"에 대한 어서션을 만들고 예기치 않은 메서드가 호출되면, 어
서션이 실패합니다. 이 기능은 예상한 호출이 이루어졌음을 어서트 할 뿐만
아니라, 추가 호출 없이 올바른 순서로 호출되었는지 확인하기 때문에 유용
합니다:

"call" 객체를 사용하여 "mock_calls"와 비교할 리스트를 구성합니다:

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True

그러나, 모의 객체를 반환하는 호출에 대한 매개 변수는 기록되지 않아서,
조상을 만드는 데 사용되는 매개 변수가 중요한 중첩된 호출을 추적할 수
없습니다:

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True


반환 값과 어트리뷰트 설정하기
-----------------------------

모의 객체에서 반환 값을 설정하는 것은 아주 간단합니다:

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3

물론 모의 객체의 메서드에 대해서도 마찬가지입니다:

>>> mock = Mock()
>>> mock.method.return_value = 3
>>> mock.method()
3

생성자에서 반환 값을 설정할 수도 있습니다:

>>> mock = Mock(return_value=3)
>>> mock()
3

모의 객체에 어트리뷰트 설정이 필요하면, 그냥 하면 됩니다:

>>> mock = Mock()
>>> mock.x = 3
>>> mock.x
3

때로는 예를 들어 "mock.connection.cursor().execute("SELECT 1")"와 같은
더 복잡한 상황을 모킹하고 싶을 수도 있습니다. 이 호출이 리스트를 반환
하도록 하려면, 중첩 호출의 결과를 구성해야 합니다.

"call"을 사용하여 다음과 같이 "연쇄 호출(chained call)"로 일련의 호출
집합을 구성할 수 있습니다:

>>> mock = Mock()
>>> cursor = mock.connection.cursor.return_value
>>> cursor.execute.return_value = ['foo']
>>> mock.connection.cursor().execute("SELECT 1")
['foo']
>>> expected = call.connection.cursor().execute("SELECT 1").call_list()
>>> mock.mock_calls
[call.connection.cursor(), call.connection.cursor().execute('SELECT 1')]
>>> mock.mock_calls == expected
True

".call_list()" 호출이 호출 객체를 연쇄 호출을 나타내는 호출 리스트로
변환합니다.


모의 객체로 예외 발생시키기
---------------------------

유용한 어트리뷰트는 "side_effect"입니다. 이것을 예외 클래스나 인스턴스
로 설정하면 모의 객체가 호출될 때 예외가 발생합니다.

>>> mock = Mock(side_effect=Exception('Boom!'))
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!


부작용 함수와 이터러블
----------------------

"side_effect"는 함수나 이터러블로 설정할 수도 있습니다. "side_effect"
의 이터러블로서의 사용 사례는 모의 객체가 여러 번 호출되고, 각 호출이
다른 값을 반환하기를 원하는 곳입니다. "side_effect"를 이터러블로 설정
하면 모의 객체에 대한 모든 호출은 이터러블에서 다음 값을 반환합니다:

>>> mock = MagicMock(side_effect=[4, 5, 6])
>>> mock()
4
>>> mock()
5
>>> mock()
6

모의 객체 호출에 전달되는 것에 따라 반환 값을 동적으로 변경하는 것과
같은 고급 사용 사례의 경우, "side_effect"는 함수가 될 수 있습니다. 함
수는 모의 객 체와 같은 인자로 호출됩니다. 함수가 반환하는 것을 호출이
반환합니다:

>>> vals = {(1, 2): 1, (2, 3): 2}
>>> def side_effect(*args):
...     return vals[args]
...
>>> mock = MagicMock(side_effect=side_effect)
>>> mock(1, 2)
1
>>> mock(2, 3)
2


비동기 이터레이터 모킹하기
--------------------------

파이썬 3.8부터, "AsyncMock"과 "MagicMock"은 "__aiter__"를 통해 비동기
이터레이터(Asynchronous Iterators)를 모킹하는 것을 지원합니다.
"__aiter__"의 "return_value" 어트리뷰트를 사용하여 이터레이션에 사용될
반환 값을 설정할 수 있습니다.

>>> mock = MagicMock()  # AsyncMock also works here
>>> mock.__aiter__.return_value = [1, 2, 3]
>>> async def main():
...     return [i async for i in mock]
...
>>> asyncio.run(main())
[1, 2, 3]


비동기 컨텍스트 관리자 모킹하기
-------------------------------

파이썬 3.8부터, "AsyncMock"과 "MagicMock"은 "__aenter__"와 "__aexit__"
를 통해 비동기 컨텍스트 관리자를 모킹하는 것을 지원합니다. 기본적으로,
"__aenter__"와 "__aexit__"는 비동기 함수를 반환하는 "AsyncMock" 인스턴
스입니다.

>>> class AsyncContextManager:
...     async def __aenter__(self):
...         return self
...     async def __aexit__(self, exc_type, exc, tb):
...         pass
...
>>> mock_instance = MagicMock(AsyncContextManager())  # AsyncMock also works here
>>> async def main():
...     async with mock_instance as result:
...         pass
...
>>> asyncio.run(main())
>>> mock_instance.__aenter__.assert_awaited_once()
>>> mock_instance.__aexit__.assert_awaited_once()


기존 객체에서 모의 객체 만들기
------------------------------

모킹을 과도하게 사용하는 한 가지 문제는 테스트를 실제 코드가 아닌 모킹
의 구현에 연결한다는 것입니다. "some_method"를 구현하는 클래스가 있다
고 가정해봅시다. 다른 클래스에 대한 테스트에서, "some_method"*도* 제공
하는 이 객체의 모의 객체를 제공합니다. 나중에 첫 번째 클래스를 리팩토
링하여, 더는 "some_method"를 갖지 않습니다 - 그러면 이제 코드가 망가졌
음에도 테스트는 계속 통과합니다!

"Mock"은 *spec* 키워드 인자를 사용하여, 모의 객체를 위한 사양으로 객체
를 제공할 수 있도록 합니다. 사양 객체에 존재하지 않는 모의 객체의 메서
드/어트리뷰트에 액세스하면 어트리뷰트 에러가 즉시 발생합니다. 사양의
구현을 변경하면, 해당 클래스를 사용하는 테스트는 해당 테스트에서 클래
스를 인스턴스 화하지 않고도 즉시 실패하기 시작합니다.

>>> mock = Mock(spec=SomeClass)
>>> mock.old_method()
Traceback (most recent call last):
   ...
AttributeError: object has no attribute 'old_method'

또한 사양을 사용하면 일부 매개 변수가 위치 인자나 이름 붙인 인자 중 어
느 것으로 전달되는지와 관계없이 모의 객체에 대한 호출을 더 스마트하게
일치시킬 수 있도록 합니다:

   >>> def f(a, b, c): pass
   ...
   >>> mock = Mock(spec=f)
   >>> mock(1, 2, 3)
   <Mock name='mock()' id='140161580456576'>
   >>> mock.assert_called_with(a=1, b=2, c=3)

이 더 스마트한 인치가 모의 객체에 대한 메서드 호출에서도 작동하게 하려
면, 자동 사양을 사용할 수 있습니다.

임의의 어트리뷰트를 읽는 것뿐만 아니라 설정하지 못하게 하는 더 강력한
사양 형식을 원하면 *spec* 대신 *spec_set*을 사용할 수 있습니다.


Using side_effect to return per file content
--------------------------------------------

"mock_open()" is used to patch "open()" method. "side_effect" can be
used to return a new Mock object per call. This can be used to return
different contents per file stored in a dictionary:

   DEFAULT = "default"
   data_dict = {"file1": "data1",
                "file2": "data2"}

   def open_side_effect(name):
       return mock_open(read_data=data_dict.get(name, DEFAULT))()

   with patch("builtins.open", side_effect=open_side_effect):
       with open("file1") as file1:
           assert file1.read() == "data1"

       with open("file2") as file2:
           assert file2.read() == "data2"

       with open("file3") as file2:
           assert file2.read() == "default"


패치 데코레이터
===============

참고:

  "patch()"를 사용할 때는 그것이 조회되는 이름 공간에서 객체를 패치하
  는 것이 중요합니다. 이것은 일반적으로 간단하지만, 빠른 안내를 위해
  패치할 곳을 읽으십시오.

테스트에서 흔히 필요한 것은 클래스 어트리뷰트나 모듈 어트리뷰트를 패치
하는 것입니다, 예를 들어 내장(builtin)을 패치하거나 모듈에 있는 인스턴
스 화 되는 클래스를 패치하는 것. 모듈과 클래스는 사실상 전역이라서, 테
스트 후에 패치를 실행 취소해야 합니다, 그렇지 않으면 패치가 다른 테스
트로 지속하여, 진단하기 어려운 문제를 일으킵니다.

mock은 세 가지 편리한 데코레이터를 제공합니다: "patch()",
"patch.object()" 및 "patch.dict()". "patch"는 패치 할 어트리뷰트를 지
정하기 위해 "package.module.Class.attribute" 형식의 단일 문자열을 취합
니다. 또한 선택적으로 어트리뷰트(또는 클래스나 무엇이건)를 바꾸려는 값
을 취합니다. 'patch.object'는 객체와 패치하려는 어트리뷰트의 이름 및
선택적으로 패치 할 값을 취합니다.

"patch.object":

   >>> original = SomeClass.attribute
   >>> @patch.object(SomeClass, 'attribute', sentinel.attribute)
   ... def test():
   ...     assert SomeClass.attribute == sentinel.attribute
   ...
   >>> test()
   >>> assert SomeClass.attribute == original

   >>> @patch('package.module.attribute', sentinel.attribute)
   ... def test():
   ...     from package.module import attribute
   ...     assert attribute is sentinel.attribute
   ...
   >>> test()

모듈("builtins"를 포함하는)을 패치하려면 "patch.object()" 대신
"patch()"를 사용하십시오:

>>> mock = MagicMock(return_value=sentinel.file_handle)
>>> with patch('builtins.open', mock):
...     handle = open('filename', 'r')
...
>>> mock.assert_called_with('filename', 'r')
>>> assert handle == sentinel.file_handle, "incorrect file handle returned"

필요하면 "package.module" 형식으로 모듈 이름을 '점으로 표시'할 수 있습
니다:

   >>> @patch('package.module.ClassName.attribute', sentinel.attribute)
   ... def test():
   ...     from package.module import ClassName
   ...     assert ClassName.attribute == sentinel.attribute
   ...
   >>> test()

좋은 패턴은 실제로 테스트 메서드 자체를 데코레이트 하는 것입니다:

>>> class MyTest(unittest.TestCase):
...     @patch.object(SomeClass, 'attribute', sentinel.attribute)
...     def test_something(self):
...         self.assertEqual(SomeClass.attribute, sentinel.attribute)
...
>>> original = SomeClass.attribute
>>> MyTest('test_something').test_something()
>>> assert SomeClass.attribute == original

Mock으로 패치하려면, 하나의 인자만으로 "patch()"를 사용할 수 있습니다
(또는 두 개의 인자로 "patch.object()"). 모의 객체가 여러분을 위해 만들
어지고 테스트 함수 / 메서드로 전달됩니다:

>>> class MyTest(unittest.TestCase):
...     @patch.object(SomeClass, 'static_method')
...     def test_something(self, mock_method):
...         SomeClass.static_method()
...         mock_method.assert_called_with()
...
>>> MyTest('test_something').test_something()

이 패턴을 사용하여 여러 패치 데코레이터를 쌓을 수 있습니다:

   >>> class MyTest(unittest.TestCase):
   ...     @patch('package.module.ClassName1')
   ...     @patch('package.module.ClassName2')
   ...     def test_something(self, MockClass2, MockClass1):
   ...         self.assertIs(package.module.ClassName1, MockClass1)
   ...         self.assertIs(package.module.ClassName2, MockClass2)
   ...
   >>> MyTest('test_something').test_something()

패치 데코레이터를 중첩할 때 모의 객체는 적용한 순서와 같은 순서(데코레
이터가 적용되는 일반적인 *파이썬* 순서)로 데코레이트 된 함수로 전달됩
니다. 이것은 밑에서 위로 올라가는 순서를 뜻해서, 위의 예에서
"test_module.ClassName2"의 모의 객체가 먼저 전달됩니다.

스코프 도중 딕셔너리에 값을 설정하고 테스트가 끝날 때 딕셔너리를 원래
상태로 복원하기 위한 "patch.dict()"도 있습니다:

>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

"patch", "patch.object" 및 "patch.dict"는 모두 컨텍스트 관리자로 사용
할 수 있습니다.

"patch()"를 사용하여 모의 객체를 만드는 곳에서, with 문의 "as" 형식을
사용하여 모의 객체에 대한 참조를 얻을 수 있습니다:

>>> class ProductionClass:
...     def method(self):
...         pass
...
>>> with patch.object(ProductionClass, 'method') as mock_method:
...     mock_method.return_value = None
...     real = ProductionClass()
...     real.method(1, 2, 3)
...
>>> mock_method.assert_called_with(1, 2, 3)

대안으로 "patch", "patch.object" 및 "patch.dict"는 클래스 데코레이터로
사용될 수 있습니다. 이런 식으로 사용될 때 이름이 "test"로 시작하는 모
든 메서드에 데코레이터를 개별적으로 적용하는 것과 같습니다.


추가 예
=======

다음은 약간 더 고급 시나리오에 대한 몇 가지 예입니다.


연쇄 호출 모킹하기
------------------

일단 "return_value" 어트리뷰트를 이해하면 연쇄 호출 모킹은 모의 객체를
사용하면 실제로 간단합니다. 모의 객체가 처음 호출되거나 호출되기 전에
"return_value"를 가져오면, 새 "Mock"이 만들어집니다.

이것은 "return_value" 모의 객체를 조사하여 모킹 된 객체에 대한 호출에
서 반환된 객체가 어떻게 사용되었는지 확인할 수 있음을 뜻합니다:

>>> mock = Mock()
>>> mock().foo(a=2, b=3)
<Mock name='mock().foo()' id='...'>
>>> mock.return_value.foo.assert_called_with(a=2, b=3)

여기서부터는 구성하고 연쇄 호출에 대한 어서션을 만드는 간단한 단계입니
다. 물론 또 다른 대안은 처음부터 더 테스트하기 쉬운 방식으로 코드를 작
성하는 것입니다...

그래서, 다음과 같은 코드가 있다고 가정합시다:

>>> class Something:
...     def __init__(self):
...         self.backend = BackendProvider()
...     def method(self):
...         response = self.backend.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
...         # more code

"BackendProvider" 가 이미 잘 테스트 되었다고 가정하면, "method()"를 어
떻게 테스트해야 할까요? 특히, 코드 섹션 "# more code"가 response 객체
를 올바른 방식으로 사용하는지 테스트하고 싶습니다.

이 호출의 연쇄는 인스턴스 어트리뷰트에서 이루어지기 때문에 "Something"
인스턴스에서 "backend" 어트리뷰트를 몽키 패치 할 수 있습니다. 이 특별
한 경우에는 "start_call"에 대한 최종 호출의 반환 값에만 관심이 있어서
해야 할 구성이 많지 않습니다. 반환하는 객체가 '파일류(file-like)'라고
가정하고, response 객체가 "spec"으로 내장 "open()"을 사용하도록 할 것
입니다.

이를 위해 모의 백 엔드로 모의 인스턴스를 만들고 모의 response 객체를
만듭니다. 응답을 최종 "start_call"의 반환 값으로 설정하기 위해 다음을
수행할 수 있습니다:

   mock_backend.get_endpoint.return_value.create_call.return_value.start_call.return_value = mock_response

"configure_mock()" 메서드를 사용하여 약간 더 좋은 방법으로 반환 값을
직접 설정할 수 있습니다:

   >>> something = Something()
   >>> mock_response = Mock(spec=open)
   >>> mock_backend = Mock()
   >>> config = {'get_endpoint.return_value.create_call.return_value.start_call.return_value': mock_response}
   >>> mock_backend.configure_mock(**config)

이것들로 우리는 "모의 백 엔드"를 몽키 패치하고 실제 호출을 할 수 있습
니다:

   >>> something.backend = mock_backend
   >>> something.method()

"mock_calls"를 사용하면 단일 어서션으로 연쇄 호출을 확인할 수 있습니다
. 연쇄 호출은 한 줄의 코드에 있는 여러 번의 호출이라서, "mock_calls"에
는 여러 항목이 있게 됩니다. "call.call_list()"를 사용하여 이 호출의 리
스트를 만들 수 있습니다:

   >>> chained = call.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
   >>> call_list = chained.call_list()
   >>> assert mock_backend.mock_calls == call_list


부분 모킹
---------

In some tests I wanted to mock out a call to "datetime.date.today()"
to return a known date, but I didn't want to prevent the code under
test from creating new date objects. Unfortunately "datetime.date" is
written in C, and so I couldn't just monkey-patch out the static
"datetime.date.today()" method.

date 클래스를 모의 객체로 효과적으로 래핑하지만, 생성자 호출은 실제 클
래스로 전달하는 (그리고 진짜 인스턴스를 반환하는) 간단한 방법을 찾았습
니다.

The "patch decorator" is used here to mock out the "date" class in the
module under test. The "side_effect" attribute on the mock date class
is then set to a lambda function that returns a real date. When the
mock date class is called a real date will be constructed and returned
by "side_effect".

   >>> from datetime import date
   >>> with patch('mymodule.date') as mock_date:
   ...     mock_date.today.return_value = date(2010, 10, 8)
   ...     mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
   ...
   ...     assert mymodule.date.today() == date(2010, 10, 8)
   ...     assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)

"datetime.date"를 전역적으로 패치하지 않았음에 유의하십시오. "date"를
*사용하는* 모듈에서 패치했습니다. 패치할 곳을 참조하십시오.

"date.today()"가 호출되면 알려진 날짜가 반환되지만, "date(...)" 생성자
에 대한 호출은 여전히 일반 date를 반환합니다. 이렇게 하지 않으면 테스
트 중인 코드와 정확히 같은 알고리즘을 사용하여 예상 결과를 계산해야 할
수 있습니다, 이는 고전적인 테스트 안티 패턴입니다.

date 생성자에 대한 호출은 "mock_date" 어트리뷰트("call_count"와 그 친
구들)에 기록되며 테스트에 유용할 수 있습니다.

date나 기타 내장 클래스 모킹을 처리하는 다른 방법은 이 블로그 페이지에
서 다루고 있습니다.


제너레이터 메서드 모킹하기
--------------------------

파이썬 제너레이터는 "yield" 문을 사용하는 함수나 메서드로, 이터레이트
할 때 일련의 값을 반환합니다 [1].

제너레이터 메서드 / 함수가 호출되면 제너레이터 객체를 반환합니다. 이터
레이트 하는 대상은 제너레이터 객체입니다. 이터레이션을 위한 프로토콜
메서드는 "__iter__()"이고, "MagicMock"을 사용하여 이를 모킹 할 수 있습
니다.

다음은 제너레이터로 구현된 "iter" 메서드를 갖는 예제 클래스입니다:

>>> class Foo:
...     def iter(self):
...         for i in [1, 2, 3]:
...             yield i
...
>>> foo = Foo()
>>> list(foo.iter())
[1, 2, 3]

이 클래스, 특히 "iter" 메서드를 어떻게 모킹할까요?

이터레이션("list" 호출로 인해 묵시적으로 이루어집니다)에서 반환된 값을
구성하려면, "foo.iter()" 호출에 의해 반환된 객체를 구성해야 합니다.

>>> mock_foo = MagicMock()
>>> mock_foo.iter.return_value = iter([1, 2, 3])
>>> list(mock_foo.iter())
[1, 2, 3]

[1] 제너레이터 표현식과 제너레이터의 더 고급 사용도 있지만, 여기서는
    다루지 않습니다. 제너레이터와 이것이 얼마나 강력한지에 대한 아주
    좋은 소개: Generator Tricks for Systems Programmers.


모든 테스트 메서드에 같은 패치 적용하기
---------------------------------------

If you want several patches in place for multiple test methods the
obvious way is to apply the patch decorators to every method. This can
feel like unnecessary repetition. Instead, you can use "patch()" (in
all its various forms) as a class decorator. This applies the patches
to all test methods on the class. A test method is identified by
methods whose names start with "test":

   >>> @patch('mymodule.SomeClass')
   ... class MyTest(unittest.TestCase):
   ...
   ...     def test_one(self, MockSomeClass):
   ...         self.assertIs(mymodule.SomeClass, MockSomeClass)
   ...
   ...     def test_two(self, MockSomeClass):
   ...         self.assertIs(mymodule.SomeClass, MockSomeClass)
   ...
   ...     def not_a_test(self):
   ...         return 'something'
   ...
   >>> MyTest('test_one').test_one()
   >>> MyTest('test_two').test_two()
   >>> MyTest('test_two').not_a_test()
   'something'

패치를 관리하는 다른 방법은 패처 메서드: start와 stop을 사용하는 것입
니다. 이를 통해 패치를 "setUp"과 "tearDown" 메서드로 옮길 수 있습니다.

   >>> class MyTest(unittest.TestCase):
   ...     def setUp(self):
   ...         self.patcher = patch('mymodule.foo')
   ...         self.mock_foo = self.patcher.start()
   ...
   ...     def test_foo(self):
   ...         self.assertIs(mymodule.foo, self.mock_foo)
   ...
   ...     def tearDown(self):
   ...         self.patcher.stop()
   ...
   >>> MyTest('test_foo').run()

이 기법을 사용하면 "stop"을 호출하여 패치가 "실행 취소"되도록 해야 합
니다. setUp에서 예외가 발생하면 tearDown이 호출되지 않기 때문에, 생각
보다 복잡할 수 있습니다. "unittest.TestCase.addCleanup()"은 이것을 더
쉽게 만듭니다:

   >>> class MyTest(unittest.TestCase):
   ...     def setUp(self):
   ...         patcher = patch('mymodule.foo')
   ...         self.addCleanup(patcher.stop)
   ...         self.mock_foo = patcher.start()
   ...
   ...     def test_foo(self):
   ...         self.assertIs(mymodule.foo, self.mock_foo)
   ...
   >>> MyTest('test_foo').run()


연결되지 않은 메서드 모킹하기
-----------------------------

오늘 테스트를 작성하는 동안 *연결되지 않은 메서드(unbound method)*를
패치해야 했습니다 (인스턴스가 아닌 클래스의 메서드를 패치하는 것입니다
). 어떤 객체가 이 특정 메서드를 호출했는지에 대한 어서션을 하고 싶기
때문에 첫 번째 인자로 self가 전달되는 것이 필요했습니다. 문제는 이것을
위해 모의 객체로 패치 할 수 없다는 것인데, 연결되지 않은 메서드를 모의
객체로 바꾸면 인스턴스에서 가져올 때 연결된 메서드가 되지 않아서, self
가 전달되지 않기 때문입니다. 해결 방법은 연결되지 않은 메서드를 실제
함수로 대신 패치하는 것입니다. "patch()" 데코레이터는 메서드를 모의 객
체로 패치하기 너무 쉽게 만들어서 실제 함수를 만들어야 하는 것이 성가십
니다.

"autospec=True"를 패치로 전달하면 *실제* 함수 객체로 패치가 수행됩니다
. 이 함수 객체는 그것이 교체하는 것과 같은 서명을 갖지만, 수면 아래에
서 모의 객체로 위임합니다. 전과 똑같은 방식으로 여전히 자동 생성된 모
의 객체를 얻습니다. 그것이 의미하는 것은, 클래스에서 연결되지 않은 메
서드를 패치하는데 사용하면 모킹 된 함수가 인스턴스에서 꺼낼 때 연결된
메서드로 바뀐다는 것입니다. "self"가 첫 번째 인자로 전달되고, 정확히
제가 원하는 것입니다:

>>> class Foo:
...   def foo(self):
...     pass
...
>>> with patch.object(Foo, 'foo', autospec=True) as mock_foo:
...   mock_foo.return_value = 'foo'
...   foo = Foo()
...   foo.foo()
...
'foo'
>>> mock_foo.assert_called_once_with(foo)

"autospec=True"를 사용하지 않으면 연결되지 않은 메서드가 대신 Mock 인
스턴스로 패치되고, "self"로 호출되지 않습니다.


모의 객체로 여러 호출 확인하기
------------------------------

mock에는 모의 객체가 어떻게 사용되는지에 대한 어서션을 만드는 데 유용
한 API가 있습니다.

>>> mock = Mock()
>>> mock.foo_bar.return_value = None
>>> mock.foo_bar('baz', spam='eggs')
>>> mock.foo_bar.assert_called_with('baz', spam='eggs')

If your mock is only being called once you can use the
"assert_called_once_with()" method that also asserts that the
"call_count" is one.

>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
>>> mock.foo_bar()
>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
Traceback (most recent call last):
    ...
AssertionError: Expected to be called once. Called 2 times.

"assert_called_with"와 "assert_called_once_with"는 모두 *가장 최근* 호
출에 대한 어서션을 합니다. 모의 객체가 여러 번 호출될 것이고, 이 호출
*모두*에 대해 어서션을 하고 싶다면 "call_args_list"를 사용할 수 있습니
다:

>>> mock = Mock(return_value=None)
>>> mock(1, 2, 3)
>>> mock(4, 5, 6)
>>> mock()
>>> mock.call_args_list
[call(1, 2, 3), call(4, 5, 6), call()]

"call" 도우미를 사용하면 이러한 호출에 대한 어서션을 쉽게 할 수 있습니
다. 예상 호출 리스트를 만들어서 "call_args_list"와 비교할 수 있습니다.
이것은 "call_args_list"의 repr과 매우 유사합니다:

>>> expected = [call(1, 2, 3), call(4, 5, 6), call()]
>>> mock.call_args_list == expected
True


가변 인자에 대처하기
--------------------

드물지만 여러분을 괴롭힐 수 있는 또 다른 상황은 모의 객체가 가변 인자
로 호출되는 경우입니다. "call_args"와 "call_args_list"는 인자에 대한 *
참조*를 저장합니다. 테스트 중인 코드에 의해 인자가 변경되면 모의 객체
가 호출되었을 때의 값에 대해 더는 어서션 할 수 없습니다.

다음은 문제를 보여주는 예제 코드입니다. 'mymodule'에 정의된 다음 함수
를 상상해보십시오:

   def frob(val):
       pass

   def grob(val):
       "First frob and then clear val"
       frob(val)
       val.clear()

"grob"이 올바른 인자로 "frob"을 호출하는지 테스트하려고 할 때 어떤 일
이 발생하는지 보십시오:

   >>> with patch('mymodule.frob') as mock_frob:
   ...     val = {6}
   ...     mymodule.grob(val)
   ...
   >>> val
   set()
   >>> mock_frob.assert_called_with({6})
   Traceback (most recent call last):
       ...
   AssertionError: Expected: (({6},), {})
   Called with: ((set(),), {})

한가지 가능성은 모의 객체가 전달한 인자를 복사하는 것일 수 있습니다.
그러면 동일성(equality)을 위해 객체 아이덴티티에 의존하는 어서션을 수
행하면 문제가 발생할 수 있습니다.

Here's one solution that uses the "side_effect" functionality. If you
provide a "side_effect" function for a mock then "side_effect" will be
called with the same args as the mock. This gives us an opportunity to
copy the arguments and store them for later assertions. In this
example I'm using *another* mock to store the arguments so that I can
use the mock methods for doing the assertion. Again a helper function
sets this up for me.

   >>> from copy import deepcopy
   >>> from unittest.mock import Mock, patch, DEFAULT
   >>> def copy_call_args(mock):
   ...     new_mock = Mock()
   ...     def side_effect(*args, **kwargs):
   ...         args = deepcopy(args)
   ...         kwargs = deepcopy(kwargs)
   ...         new_mock(*args, **kwargs)
   ...         return DEFAULT
   ...     mock.side_effect = side_effect
   ...     return new_mock
   ...
   >>> with patch('mymodule.frob') as mock_frob:
   ...     new_mock = copy_call_args(mock_frob)
   ...     val = {6}
   ...     mymodule.grob(val)
   ...
   >>> new_mock.assert_called_with({6})
   >>> new_mock.call_args
   call({6})

"copy_call_args"는 호출될 모의로 호출됩니다. 어서션을 수행할 새로운 모
의 객체를 반환합니다. "side_effect" 함수는 인자의 사본을 만들고 사본으
로 "new_mock"을 호출합니다.

참고:

  모의 객체를 한 번만 사용하려는 경우 호출 시점에서 인자를 확인하는 쉬
  운 방법이 있습니다. 단순히 "side_effect" 함수 내에서 확인할 수 있습
  니다.

  >>> def side_effect(arg):
  ...     assert arg == {6}
  ...
  >>> mock = Mock(side_effect=side_effect)
  >>> mock({6})
  >>> mock(set())
  Traceback (most recent call last):
      ...
  AssertionError

다른 접근법은 인자를 복사("copy.deepcopy()"를 사용해서)하는 "Mock"이나
"MagicMock"의 서브 클래스를 만드는 것입니다. 구현 예는 다음과 같습니다
:

>>> from copy import deepcopy
>>> class CopyingMock(MagicMock):
...     def __call__(self, /, *args, **kwargs):
...         args = deepcopy(args)
...         kwargs = deepcopy(kwargs)
...         return super().__call__(*args, **kwargs)
...
>>> c = CopyingMock(return_value=None)
>>> arg = set()
>>> c(arg)
>>> arg.add(1)
>>> c.assert_called_with(set())
>>> c.assert_called_with(arg)
Traceback (most recent call last):
    ...
AssertionError: Expected call: mock({1})
Actual call: mock(set())
>>> c.foo
<CopyingMock name='mock.foo' id='...'>

"Mock"이나 "MagicMock"을 서브 클래싱할 때 모든 동적으로 만들어진 어트
리뷰트와 "return_value"는 자동으로 서브 클래스를 사용합니다. 즉,
"CopyingMock"의 모든 자식도 "CopyingMock" 형이 됩니다.


중첩 패치
---------

patch를 컨텍스트 관리자로 것이 멋지기는 하지만, 여러 패치를 수행하면
오른쪽으로 점점 더 들여쓰기 되는 문장으로 중첩될 수 있습니다:

   >>> class MyTest(unittest.TestCase):
   ...
   ...     def test_foo(self):
   ...         with patch('mymodule.Foo') as mock_foo:
   ...             with patch('mymodule.Bar') as mock_bar:
   ...                 with patch('mymodule.Spam') as mock_spam:
   ...                     assert mymodule.Foo is mock_foo
   ...                     assert mymodule.Bar is mock_bar
   ...                     assert mymodule.Spam is mock_spam
   ...
   >>> original = mymodule.Foo
   >>> MyTest('test_foo').test_foo()
   >>> assert mymodule.Foo is original

unittest "cleanup" 함수와 패처 메서드: start와 stop을 사용하면 중첩된
들여쓰기 없이 같은 효과를 얻을 수 있습니다. 간단한 도우미 메서드인
"create_patch"는 제 자리에서 패치를 설치하고 만들어진 모의 객체를 반환
합니다:

   >>> class MyTest(unittest.TestCase):
   ...
   ...     def create_patch(self, name):
   ...         patcher = patch(name)
   ...         thing = patcher.start()
   ...         self.addCleanup(patcher.stop)
   ...         return thing
   ...
   ...     def test_foo(self):
   ...         mock_foo = self.create_patch('mymodule.Foo')
   ...         mock_bar = self.create_patch('mymodule.Bar')
   ...         mock_spam = self.create_patch('mymodule.Spam')
   ...
   ...         assert mymodule.Foo is mock_foo
   ...         assert mymodule.Bar is mock_bar
   ...         assert mymodule.Spam is mock_spam
   ...
   >>> original = mymodule.Foo
   >>> MyTest('test_foo').run()
   >>> assert mymodule.Foo is original


MagicMock으로 딕셔너리 모킹하기
-------------------------------

딕셔너리나 다른 컨테이너 객체를 모킹하여, 여전히 딕셔너리처럼 동작하면
서 이에 대한 모든 액세스를 기록하고 싶을 수 있습니다.

딕셔너리처럼 동작하는 "MagicMock"을 사용하고 "side_effect"가 딕셔너리
액세스를 우리의 제어하에 있는 실제 하부 딕셔너리로 위임하게 해서 목적
을 달성할 수 있습니다.

When the "__getitem__()" and "__setitem__()" methods of our
"MagicMock" are called (normal dictionary access) then "side_effect"
is called with the key (and in the case of "__setitem__" the value
too). We can also control what is returned.

"MagicMock"이 사용된 후에 "call_args_list"와 같은 어트리뷰트를 사용하
여 딕셔너리가 어떻게 사용되었는지를 어서트할 수 있습니다:

>>> my_dict = {'a': 1, 'b': 2, 'c': 3}
>>> def getitem(name):
...      return my_dict[name]
...
>>> def setitem(name, val):
...     my_dict[name] = val
...
>>> mock = MagicMock()
>>> mock.__getitem__.side_effect = getitem
>>> mock.__setitem__.side_effect = setitem

참고:

  "MagicMock"을 사용하는 대신 "Mock"을 사용하고 *오직* 원하는 매직 메
  서드만 제공할 수 있습니다:

  >>> mock = Mock()
  >>> mock.__getitem__ = Mock(side_effect=getitem)
  >>> mock.__setitem__ = Mock(side_effect=setitem)

  *세 번째* 옵션은 "MagicMock"을 사용하지만, "dict"를 *spec* (또는
  *spec_set*) 인자로 전달하여 만들어진 "MagicMock"이 딕셔너리 매직 메
  서드 만 갖도록 하는 것입니다:

  >>> mock = MagicMock(spec_set=dict)
  >>> mock.__getitem__.side_effect = getitem
  >>> mock.__setitem__.side_effect = setitem

이러한 부작용 함수가 제자리에 들어가면, "mock"은 일반 딕셔너리처럼 작
동하지만, 액세스를 기록합니다. 존재하지 않는 키에 액세스하려고 하면
"KeyError"를 발생시키기조차 합니다.

>>> mock['a']
1
>>> mock['c']
3
>>> mock['d']
Traceback (most recent call last):
    ...
KeyError: 'd'
>>> mock['b'] = 'fish'
>>> mock['d'] = 'eggs'
>>> mock['b']
'fish'
>>> mock['d']
'eggs'

사용된 후에 일반적인 모의 객체 메서드와 어트리뷰트를 사용하여 액세스에
대한 어서션을 할 수 있습니다:

>>> mock.__getitem__.call_args_list
[call('a'), call('c'), call('d'), call('b'), call('d')]
>>> mock.__setitem__.call_args_list
[call('b', 'fish'), call('d', 'eggs')]
>>> my_dict
{'a': 1, 'b': 'fish', 'c': 3, 'd': 'eggs'}


Mock 서브 클래스와 그 어트리뷰트
--------------------------------

"Mock"을 서브 클래싱하려는 여러 가지 이유가 있습니다. 한 가지 이유는
도우미 메서드를 추가하는 것입니다. 다음은 시시한 예입니다:

>>> class MyMock(MagicMock):
...     def has_been_called(self):
...         return self.called
...
>>> mymock = MyMock(return_value=None)
>>> mymock
<MyMock id='...'>
>>> mymock.has_been_called()
False
>>> mymock()
>>> mymock.has_been_called()
True

"Mock" 인스턴스의 표준 동작은 어트리뷰트와 반환 값 모의 객체가 액세스
되는 모의 객체의 형과 같은 형이라는 것입니다. 이를 통해 "Mock" 어트리
뷰트는 "Mock"이고 "MagicMock" 어트리뷰트는 "MagicMock"이 됩니다 [2].
따라서 도우미 메서드를 추가하기 위해 서브 클래싱하면 이 메서드는 서브
클래스 인스턴스의 어트리뷰트와 반환 값 모의 객체에서도 사용할 수 있습
니다.

>>> mymock.foo
<MyMock name='mock.foo' id='...'>
>>> mymock.foo.has_been_called()
False
>>> mymock.foo()
<MyMock name='mock.foo()' id='...'>
>>> mymock.foo.has_been_called()
True

Sometimes this is inconvenient. For example, one user is subclassing
mock to created a Twisted adaptor. Having this applied to attributes
too actually causes errors.

"Mock"(이것의 모든 종류에서)은 "_get_child_mock"이라는 메서드를 사용하
여 어트리뷰트와 반환 값을 위한 이러한 "서브 모의 객체"를 만듭니다. 이
메서드를 재정의하여 서브 클래스가 어트리뷰트에 사용되지 않도록 할 수
있습니다. 서명은 모의 객체 생성자에 전달되는 임의의 키워드 인자
("**kwargs")를 취하는 것입니다:

>>> class Subclass(MagicMock):
...     def _get_child_mock(self, /, **kwargs):
...         return MagicMock(**kwargs)
...
>>> mymock = Subclass()
>>> mymock.foo
<MagicMock name='mock.foo' id='...'>
>>> assert isinstance(mymock, Subclass)
>>> assert not isinstance(mymock.foo, Subclass)
>>> assert not isinstance(mymock(), Subclass)

[2] 이 규칙의 예외는 콜러블이 아닌 모의 객체입니다. 어트리뷰트는 콜러
    블 변형을 사용하는데, 그렇지 않으면 콜러블이 아닌 모의 객체가 콜러
    블 메서드를 가질 수 없기 때문입니다.


patch.dict로 임포트를 모킹하기
------------------------------

모킹이 어려울 수 있는 한 가지 상황은 함수 내부에 지역 임포트가 있는 경
우입니다. 이것들은 모킹하기가 더 어려운데, 우리가 패치 할 수 있는 모듈
이름 공간의 객체를 사용하지 않기 때문입니다.

일반적으로 지역 임포트는 피해야 합니다. 그것들은 때때로 순환 의존성을
막기 위해 수행되는데, *보통* 문제를 해결하는 더 좋은 방법(코드를 리팩
터 하십시오)이 있습니다. 또는 임포트를 지연시켜서 "선불 비용"을 방지하
기 위해 수행합니다. 이 또한 무조건적인 지역 임포트보다 더 나은 방법으
로 해결할 수 있습니다 (모듈을 클래스나 모듈 어트리뷰트로 저장하고 처음
사용할 때만 임포트 합니다).

That aside there is a way to use "mock" to affect the results of an
import. Importing fetches an *object* from the "sys.modules"
dictionary. Note that it fetches an *object*, which need not be a
module. Importing a module for the first time results in a module
object being put in "sys.modules", so usually when you import
something you get a module back. This need not be the case however.

즉, "patch.dict()"를 사용하여 *임시로* "sys.modules"에 모의 객체를 넣
을 수 있습니다. 이 패치가 활성화되어있는 동안 모든 임포트는 모의 객체
를 가져옵니다. 패치가 완료되면 (데코레이트 된 함수가 종료되거나, with
문 본문이 완료되거나 "patcher.stop()"이 호출되면) 이전에 있던 모든 것
이 안전하게 복원됩니다.

다음은 'fooble' 모듈을 모킹하는 예입니다.

>>> import sys
>>> mock = Mock()
>>> with patch.dict('sys.modules', {'fooble': mock}):
...    import fooble
...    fooble.blob()
...
<Mock name='mock.blob()' id='...'>
>>> assert 'fooble' not in sys.modules
>>> mock.blob.assert_called_once_with()

보시다시피 "import fooble"은 성공하지만, 끝났을 때 "sys.modules"에는
'fooble'이 남아 있지 않습니다.

이것은 "from module import name" 형식에서도 작동합니다:

>>> mock = Mock()
>>> with patch.dict('sys.modules', {'fooble': mock}):
...    from fooble import blob
...    blob.blip()
...
<Mock name='mock.blob.blip()' id='...'>
>>> mock.blob.blip.assert_called_once_with()

약간의 추가 작업으로 패키지 임포트를 모킹 할 수도 있습니다:

>>> mock = Mock()
>>> modules = {'package': mock, 'package.module': mock.module}
>>> with patch.dict('sys.modules', modules):
...    from package.module import fooble
...    fooble()
...
<Mock name='mock.module.fooble()' id='...'>
>>> mock.module.fooble.assert_called_once_with()


호출 순서 추적과 덜 상세한 호출 어서션
--------------------------------------

"Mock" 클래스를 사용하면 "method_calls" 어트리뷰트를 통해 모의 객체에
서 메서드 호출의 *순서*를 추적할 수 있습니다. 개별 모의 객체 간의 호출
순서를 추적할 수는 없지만, "mock_calls"를 사용하여 같은 효과를 얻을 수
있습니다.

모의 객체들은 "mock_calls"에서 자식 모의 객체에 대한 호출을 추적하고,
모의 객체의 임의 어트리뷰트에 액세스하면 자식 모의 객체를 만들기 때문
에, 부모 모의 객체로부터 개별 모의 객체를 만들 수 있습니다. 그러면 해
당 자식 모의 객체에 대한 호출은 부모의 "mock_calls"에 순서대로 기록됩
니다:

>>> manager = Mock()
>>> mock_foo = manager.foo
>>> mock_bar = manager.bar

>>> mock_foo.something()
<Mock name='mock.foo.something()' id='...'>
>>> mock_bar.other.thing()
<Mock name='mock.bar.other.thing()' id='...'>

>>> manager.mock_calls
[call.foo.something(), call.bar.other.thing()]

그런 다음 관리자 모의 객체의 "mock_calls" 어트리뷰트와 비교하여 순서를
포함하여 호출에 대해 어서션 할 수 있습니다:

>>> expected_calls = [call.foo.something(), call.bar.other.thing()]
>>> manager.mock_calls == expected_calls
True

"patch"가 모의 객체를 만들고 제자리에 배치하는 경우, "attach_mock()"
메서드를 사용하여 모의 객체를 관리자 모의 객체에 연결할 수 있습니다.
연결 후 호출은 관리자의 "mock_calls"에 기록됩니다.

   >>> manager = MagicMock()
   >>> with patch('mymodule.Class1') as MockClass1:
   ...     with patch('mymodule.Class2') as MockClass2:
   ...         manager.attach_mock(MockClass1, 'MockClass1')
   ...         manager.attach_mock(MockClass2, 'MockClass2')
   ...         MockClass1().foo()
   ...         MockClass2().bar()
   <MagicMock name='mock.MockClass1().foo()' id='...'>
   <MagicMock name='mock.MockClass2().bar()' id='...'>
   >>> manager.mock_calls
   [call.MockClass1(),
   call.MockClass1().foo(),
   call.MockClass2(),
   call.MockClass2().bar()]

많은 호출이 이루어졌지만, 특정 시퀀스에만 관심이 있다면 대안은
"assert_has_calls()" 메서드를 사용하는 것입니다. 이것은 호출 리스트를
취합니다 ("call" 객체로 구성됩니다). 해당 호출 시퀀스가 "mock_calls"에
있으면 어서션이 성공합니다.

>>> m = MagicMock()
>>> m().foo().bar().baz()
<MagicMock name='mock().foo().bar().baz()' id='...'>
>>> m.one().two().three()
<MagicMock name='mock.one().two().three()' id='...'>
>>> calls = call.one().two().three().call_list()
>>> m.assert_has_calls(calls)

연쇄 호출 "m.one().two().three()"가 모의 객체에 대한 호출의 전부는 아
니지만, 어서션이 여전히 성공합니다.

때로는 모의 객체에 여러 번의 호출이 있을 수 있고, 그 호출들의 *일부*에
대만 어서션에만 관심이 있을 수 있습니다. 순서에 신경 쓰지 않을 수도 있
습니다. 이 경우 "any_order=True"를 "assert_has_calls"로 전달할 수 있습
니다:

>>> m = MagicMock()
>>> m(1), m.two(2, 3), m.seven(7), m.fifty('50')
(...)
>>> calls = [call.fifty('50'), call(1), call.seven(7)]
>>> m.assert_has_calls(calls, any_order=True)


더 복잡한 인자 일치
-------------------

"ANY"와 같은 기본 개념을 사용하여 모의 객체에 인자로 사용되는 객체에
대해 더 복잡한 어서션을 수행하도록 매처(matchers)를 구현할 수 있습니다
.

기본적으로 객체 아이덴티티에 기반하여 를 기준으로 같다고 비교되는 (이
것이 사용자 정의 클래스의 파이썬 기본 동작입니다) 어떤 객체가 모의 객
체로 전달되기를 기대한다고 가정합시다. "assert_called_with()"를 사용하
려면 정확히 같은 객체를 전달해야 합니다. 이 객체의 일부 어트리뷰트에만
관심이 있다면 이러한 어트리뷰트를 확인하는 매처를 만들 수 있습니다.

이 예에서 "assert_called_with"에 대한 '표준' 호출이 충분하지 않다는 것
을 볼 수 있습니다:

>>> class Foo:
...     def __init__(self, a, b):
...         self.a, self.b = a, b
...
>>> mock = Mock(return_value=None)
>>> mock(Foo(1, 2))
>>> mock.assert_called_with(Foo(1, 2))
Traceback (most recent call last):
    ...
AssertionError: Expected: call(<__main__.Foo object at 0x...>)
Actual call: call(<__main__.Foo object at 0x...>)

"Foo" 클래스를 위한 비교 함수는 다음과 같습니다:

>>> def compare(self, other):
...     if not type(self) == type(other):
...         return False
...     if self.a != other.a:
...         return False
...     if self.b != other.b:
...         return False
...     return True
...

그리고 동등 비교 연산에 이와 같은 비교 함수를 사용할 수 있는 매처 객체
는 다음과 같습니다:

>>> class Matcher:
...     def __init__(self, compare, some_obj):
...         self.compare = compare
...         self.some_obj = some_obj
...     def __eq__(self, other):
...         return self.compare(self.some_obj, other)
...

이 모든 것을 종합하면:

>>> match_foo = Matcher(compare, Foo(1, 2))
>>> mock.assert_called_with(match_foo)

"Matcher"는 compare 함수와 비교하려는 "Foo" 객체로 인스턴스 화 됩니다.
"assert_called_with"에서는 "Matcher" 동등 비교 메서드가 호출되는데, 이
메서드는 모의 객체가 호출된 객체와 우리가 매처를 만들 때 제공한 객체를
비교합니다. 일치하면 "assert_called_with"가 통과하고, 그렇지 않으면
"AssertionError"가 발생합니다:

>>> match_wrong = Matcher(compare, Foo(3, 4))
>>> mock.assert_called_with(match_wrong)
Traceback (most recent call last):
    ...
AssertionError: Expected: ((<Matcher object at 0x...>,), {})
Called with: ((<Foo object at 0x...>,), {})

약간의 조정만으로 비교 함수가 "AssertionError"를 직접 발생시키고 더 유
용한 실패 메시지를 제공할 수 있습니다.

버전 1.5부터, 파이썬 테스트 라이브러리 PyHamcrest는 여기에서 유용할 수
있는 유사한 기능을 동등 비교 매처의 형태로 제공합니다
(hamcrest.library.integration.match_equality).
