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
) 사용 방법에 대한 어서션을 만들 수 있도록 하는 메서드와 어트리뷰트를 제공합니다.
일단 모의 객체가 호출되면 그것의 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"
패치 데코레이터¶
테스트에서 흔히 필요한 것은 클래스 어트리뷰트나 모듈 어트리뷰트를 패치하는 것입니다, 예를 들어 내장(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]
모든 테스트 메서드에 같은 패치 적용하기¶
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)
이 규칙의 예외는 콜러블이 아닌 모의 객체입니다. 어트리뷰트는 콜러블 변형을 사용하는데, 그렇지 않으면 콜러블이 아닌 모의 객체가 콜러블 메서드를 가질 수 없기 때문입니다.
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).