Как проверить, что метод был вызван на мок-объекте?

Для проверки вызова метода на мок-объекте можно использовать различные методы библиотеки unittest.mock:
  • mock_obj.called: Проверяет, был ли вызван мок-объект хотя бы раз.
  • mock_obj.assert_called(): Проверяет, что мок-объект был вызван. Если не был, выбрасывает исключение AssertionError.
  • mock_obj.assert_called_once(): Проверяет, что мок-объект был вызван ровно один раз.
  • mock_obj.assert_called_with(*args, **kwargs): Проверяет, что мок-объект был вызван с указанными аргументами.
  • mock_obj.assert_called_once_with(*args, **kwargs): Комбинация assert_called_once и assert_called_with.
  • mock_obj.call_count: Возвращает количество вызовов мок-объекта.
  • mock_obj.call_args: Хранит последние аргументы, с которыми был вызван мок.
  • mock_obj.call_args_list: Хранит список всех вызовов с аргументами.
Пример:
    
      from unittest.mock import Mock

      mock_obj = Mock()
      mock_obj.my_method(1, arg='value')

      mock_obj.my_method.assert_called_with(1, arg='value')
    
  

Для проверки, был ли вызван метод на мок-объекте в Python, обычно используют библиотеку unittest.mock (или mock для Python 2).

Вот основные способы и примеры:

  1. assert_called(): Проверяет, был ли метод вызван хотя бы один раз.

    import unittest
    from unittest.mock import Mock
    
    class MyTest(unittest.TestCase):
        def test_method_called(self):
            mock_obj = Mock()
            mock_obj.my_method()
            mock_obj.my_method.assert_called() # Утверждение, что метод был вызван
  2. assert_not_called(): Проверяет, что метод не был вызван.

    import unittest
    from unittest.mock import Mock
    
    class MyTest(unittest.TestCase):
        def test_method_not_called(self):
            mock_obj = Mock()
            #mock_obj.my_method()  # Метод не вызывается
            mock_obj.my_method.assert_not_called() # Утверждение, что метод не был вызван
  3. assert_called_once(): Проверяет, что метод был вызван ровно один раз.

    import unittest
    from unittest.mock import Mock
    
    class MyTest(unittest.TestCase):
        def test_method_called_once(self):
            mock_obj = Mock()
            mock_obj.my_method()
            mock_obj.my_method.assert_called_once() # Утверждение, что метод был вызван один раз
  4. assert_called_with(*args, **kwargs): Проверяет, что метод был вызван с определенными аргументами.

    import unittest
    from unittest.mock import Mock
    
    class MyTest(unittest.TestCase):
        def test_method_called_with(self):
            mock_obj = Mock()
            mock_obj.my_method(1, "hello", arg2=True)
            mock_obj.my_method.assert_called_with(1, "hello", arg2=True) # Утверждение о вызове с аргументами
  5. assert_called_once_with(*args, **kwargs): Проверяет, что метод был вызван ровно один раз с определенными аргументами.

    import unittest
    from unittest.mock import Mock
    
    class MyTest(unittest.TestCase):
        def test_method_called_once_with(self):
            mock_obj = Mock()
            mock_obj.my_method(1, "hello", arg2=True)
            mock_obj.my_method.assert_called_once_with(1, "hello", arg2=True) # Утверждение о вызове один раз с аргументами
  6. assert_any_call(*args, **kwargs): Проверяет, что метод был вызван хотя бы один раз с указанными аргументами (не обязательно последним вызовом).

    import unittest
    from unittest.mock import Mock, call
    
    class MyTest(unittest.TestCase):
        def test_method_any_call(self):
            mock_obj = Mock()
            mock_obj.my_method(1)
            mock_obj.my_method(2)
            mock_obj.my_method.assert_any_call(2) # Утверждение, что был вызов с аргументом 2 (не обязательно последний)
  7. assert_has_calls(calls): Проверяет, что метод был вызван с определенной последовательностью вызовов. Порядок важен.

    import unittest
    from unittest.mock import Mock, call
    
    class MyTest(unittest.TestCase):
        def test_method_has_calls(self):
            mock_obj = Mock()
            mock_obj.my_method(1)
            mock_obj.my_method(2)
            calls = [call(1), call(2)]
            mock_obj.my_method.assert_has_calls(calls) # Утверждение о последовательности вызовов
        
  8. mock_calls: Это атрибут, который содержит список всех вызовов метода. Его можно использовать для более гибких проверок, особенно если стандартных `assert` методов недостаточно.

    import unittest
    from unittest.mock import Mock, call
    
    class MyTest(unittest.TestCase):
        def test_method_mock_calls(self):
            mock_obj = Mock()
            mock_obj.my_method(1)
            mock_obj.my_method(2, arg="value")
    
            expected_calls = [call.my_method(1), call.my_method(2, arg="value")]
            self.assertEqual(mock_obj.mock_calls, expected_calls)
        

Важно: Убедитесь, что вы импортировали unittest, если используете unittest.TestCase для тестов.

Пример полного тестового класса:

import unittest
from unittest.mock import Mock

class MyClass:
    def do_something(self, a, b):
        return a + b

    def another_method(self):
        pass

class TestMyClass(unittest.TestCase):
    def test_do_something(self):
        # Mock MyClass
        my_obj = Mock(spec=MyClass) # Лучше использовать spec для типобезопасности

        # Configure the mock to return a specific value
        my_obj.do_something.return_value = 5

        # Call the method (or a function that uses the method)
        result = my_obj.do_something(2, 3)

        # Assert that the method was called with the correct arguments
        my_obj.do_something.assert_called_with(2, 3)

        # Assert that the method was called once
        my_obj.do_something.assert_called_once()

        # Assert the return value
        self.assertEqual(result, 5)

    def test_another_method(self):
        my_obj = Mock(spec=MyClass)
        my_obj.another_method()
        my_obj.another_method.assert_called()
        my_obj.another_method.assert_called_once()


if __name__ == '__main__':
    unittest.main()

Рекомендации:

  • Используйте spec при создании моков, чтобы убедиться, что мок-объект имеет те же атрибуты и методы, что и настоящий объект. Это улучшает типобезопасность и помогает избежать ошибок, связанных с опечатками в именах методов.
  • Пишите unit-тесты, которые покрывают различные сценарии использования метода, чтобы убедиться, что он работает правильно при разных входных данных.
  • Используйте pytest (с плагином pytest-mock) для более лаконичного синтаксиса и дополнительных возможностей мокирования.
0