Programing

모의 메소드에 대한 연속 호출 주장

lottogame 2020. 6. 25. 08:02
반응형

모의 메소드에 대한 연속 호출 주장


모의에게 유용한 assert_called_with()방법이 있습니다. 그러나 내가 이해 하는 한 메소드에 대한 마지막 호출 만 확인합니다 .
매번 다른 매개 변수를 사용하여 mocked 메서드를 3 번 ​​연속적으로 호출하는 코드가있는 경우 특정 매개 변수를 사용 하여이 3 호출을 어설 션하려면 어떻게해야합니까?


assert_has_calls 이 문제에 대한 또 다른 접근법입니다.

문서에서 :

assert_has_calls (호출, any_order = False)

지정된 호출로 모의가 호출되었다고 주장하십시오. mock_calls 목록에서 호출을 확인합니다.

any_order가 False (기본값)이면 호출은 순차적이어야합니다. 지정된 통화 전후에 추가 통화가있을 수 있습니다.

any_order가 True이면 호출 순서는 상관 없지만 모두 mock_calls에 나타나야합니다.

예:

>>> from mock import call, Mock
>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)

출처 : https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_has_calls


보통, 나는 통화 순서에 신경 쓰지 않고 단지 그들이 일어난 일에만 신경 쓰지 않습니다. 이 경우에 assert_any_call대한 어설 션과 결합 call_count합니다.

>>> import mock
>>> m = mock.Mock()
>>> m(1)
<Mock name='mock()' id='37578160'>
>>> m(2)
<Mock name='mock()' id='37578160'>
>>> m(3)
<Mock name='mock()' id='37578160'>
>>> m.assert_any_call(1)
>>> m.assert_any_call(2)
>>> m.assert_any_call(3)
>>> assert 3 == m.call_count
>>> m.assert_any_call(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "[python path]\lib\site-packages\mock.py", line 891, in assert_any_call
    '%s call not found' % expected_string
AssertionError: mock(4) call not found

단일 메서드에 전달 된 많은 호출 목록보다 읽기 쉽고 이해하기 쉽도록이 방법을 사용합니다.

주문에 관심이 있거나 동일한 전화가 여러 번 예상되는 assert_has_calls것이 더 적절할 수 있습니다.

편집하다

이 답변을 게시 한 이후 일반적인 테스트 방법을 다시 생각했습니다. 테스트가 복잡해지면 부적절하게 테스트 중이거나 디자인에 문제가있을 수 있습니다. Mocks는 객체 지향 디자인에서 객체 간 통신을 테스트하도록 설계되었습니다. 디자인이 객체 지향적이지 않은 경우 (더 절차 적 또는 기능적) 모의는 완전히 부적절 할 수 있습니다. 메소드 내부에서 너무 많은 일이 발생했거나 가장 잘 조롱되지 않은 내부 세부 사항을 테스트하는 중일 수 있습니다. 내 코드가 객체 지향적이지 않을 때이 방법에서 언급 된 전략을 개발했으며, 가장 잘 드러나지 않은 내부 세부 정보도 테스트하고 있다고 생각합니다.


Mock.call_args_list속성사용하여 매개 변수를 이전 메소드 호출과 비교할 수 있습니다 . Mock.call_count속성 과 함께 모든 권한을 부여해야합니다.


나는 항상 이것을 한 번에 다시 찾아야하므로 여기에 내 대답이 있습니다.


같은 클래스의 다른 객체에 대해 여러 개의 메소드 호출

우리가 무거운 클래스를 가지고 있다고 가정 해보십시오 (우리가 조롱하고 싶어).

In [1]: class HeavyDuty(object):
   ...:     def __init__(self):
   ...:         import time
   ...:         time.sleep(2)  # <- Spends a lot of time here
   ...:     
   ...:     def do_work(self, arg1, arg2):
   ...:         print("Called with %r and %r" % (arg1, arg2))
   ...:  

다음은 HeavyDuty클래스의 두 인스턴스를 사용하는 코드입니다 .

In [2]: def heavy_work():
   ...:     hd1 = HeavyDuty()
   ...:     hd1.do_work(13, 17)
   ...:     hd2 = HeavyDuty()
   ...:     hd2.do_work(23, 29)
   ...:    


이제 heavy_work함수에 대한 테스트 사례가 있습니다.

In [3]: from unittest.mock import patch, call
   ...: def test_heavy_work():
   ...:     expected_calls = [call.do_work(13, 17),call.do_work(23, 29)]
   ...:     
   ...:     with patch('__main__.HeavyDuty') as MockHeavyDuty:
   ...:         heavy_work()
   ...:         MockHeavyDuty.return_value.assert_has_calls(expected_calls)
   ...:  

We are mocking the HeavyDuty class with MockHeavyDuty. To assert method calls coming from every HeavyDuty instance we have to refer to MockHeavyDuty.return_value.assert_has_calls, instead of MockHeavyDuty.assert_has_calls. In addition, in the list of expected_calls we have to specify which method name we are interested in asserting calls for. So our list is made of calls to call.do_work, as opposed to simply call.

Exercising the test case shows us it is successful:

In [4]: print(test_heavy_work())
None


If we modify the heavy_work function, the test fails and produces a helpful error message:

In [5]: def heavy_work():
   ...:     hd1 = HeavyDuty()
   ...:     hd1.do_work(113, 117)  # <- call args are different
   ...:     hd2 = HeavyDuty()
   ...:     hd2.do_work(123, 129)  # <- call args are different
   ...:     

In [6]: print(test_heavy_work())
---------------------------------------------------------------------------
(traceback omitted for clarity)

AssertionError: Calls not found.
Expected: [call.do_work(13, 17), call.do_work(23, 29)]
Actual: [call.do_work(113, 117), call.do_work(123, 129)]


Asserting multiple calls to a function

To contrast with the above, here is an example that shows how to mock multiple calls to a function:

In [7]: def work_function(arg1, arg2):
   ...:     print("Called with args %r and %r" % (arg1, arg2))

In [8]: from unittest.mock import patch, call
   ...: def test_work_function():
   ...:     expected_calls = [call(13, 17), call(23, 29)]    
   ...:     with patch('__main__.work_function') as mock_work_function:
   ...:         work_function(13, 17)
   ...:         work_function(23, 29)
   ...:         mock_work_function.assert_has_calls(expected_calls)
   ...:    

In [9]: print(test_work_function())
None


There are two main differences. The first one is that when mocking a function we setup our expected calls using call, instead of using call.some_method. The second one is that we call assert_has_calls on mock_work_function, instead of on mock_work_function.return_value.

참고URL : https://stackoverflow.com/questions/7242433/asserting-successive-calls-to-a-mock-method

반응형