Использование мок-объектов для симуляции отказов и ошибок в распределённых системах – мощный метод для тестирования устойчивости и отказоустойчивости вашего кода. Моки позволяют имитировать непредсказуемое поведение внешних сервисов или компонентов без необходимости запуска реальных аналогов, что экономит ресурсы и упрощает воспроизведение сложных сценариев.
Основные принципы:
Примеры использования в распределённых системах:
session.commit() так, чтобы он вызывал исключение sqlalchemy.exc.OperationalError.requests.get() так, чтобы она возвращала объект Response с кодом статуса 503.publish() у объекта брокера сообщений так, чтобы он вызывал исключение pika.exceptions.AMQPConnectionError.get() у объекта Redis так, чтобы он возвращал None.acquire() у объекта блокировки так, чтобы он всегда возвращал False.Пример кода (использование unittest.mock):
  import unittest
  from unittest.mock import patch
  import requests
  def get_data_from_api(url):
      response = requests.get(url)
      response.raise_for_status()  # Raises HTTPError for bad responses (4xx or 5xx)
      return response.json()
  class TestGetDataFromApi(unittest.TestCase):
      @patch('requests.get')
      def test_api_unavailable(self, mock_get):
          mock_get.side_effect = requests.exceptions.ConnectionError("Mocked connection error")
          with self.assertRaises(requests.exceptions.ConnectionError):
              get_data_from_api("https://example.com/api")
      @patch('requests.get')
      def test_api_returns_error(self, mock_get):
          mock_response = unittest.mock.Mock()
          mock_response.status_code = 500
          mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError("Mocked 500 error")  # Make it raise an exception on raise_for_status
          mock_get.return_value = mock_response
          with self.assertRaises(requests.exceptions.HTTPError):
              get_data_from_api("https://example.com/api")
      @patch('requests.get')
      def test_api_returns_valid_data(self, mock_get):
          mock_response = unittest.mock.Mock()
          mock_response.status_code = 200
          mock_response.json.return_value = {"key": "value"}
          mock_get.return_value = mock_response
          data = get_data_from_api("https://example.com/api")
          self.assertEqual(data, {"key": "value"})
  if __name__ == '__main__':
      unittest.main()
  Ключевые моменты в примере:
@patch('requests.get'):  Используем декоратор patch из модуля unittest.mock для замены функции requests.get мок-объектом.mock_get.side_effect = ...:  Устанавливаем side_effect мок-объекта, чтобы он вызывал исключение requests.exceptions.ConnectionError.  Это имитирует ситуацию, когда API недоступен.mock_response.status_code = 500 и mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError(...): Имитируем ответ сервера с кодом 500 и гарантируем вызов исключения при попытке проверки статуса (raise_for_status).mock_response.json.return_value = {"key": "value"}: Задаем возвращаемое значение метода json() мок-объекта, чтобы имитировать успешный ответ API с данными.Рекомендации:
with patch(...)) для мокирования.unittest.mock предоставляет методы для этого (например, mock_object.assert_called_with(...)).responses. Они предоставляют более удобный и декларативный способ мокирования HTTP-запросов и ответов.Мокирование отказов и ошибок – важная часть разработки надежных распределенных систем. Оно позволяет выявлять и устранять уязвимости в вашем коде до того, как они возникнут в рабочей среде.