Как протестировать обработку исключений, если не удается вызвать исключение во время тестирования?

Я работаю над набором представлений Django REST Framework (DRF), который включает обработку исключений для решения непредвиденных ошибок. В наборе представлений есть метод действия, который извлекает информацию о ценах на серверах из стороннего API. Вот упрощенная версия кода:

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
import logging

logger = logging.getLogger(__name__)

class MyViewSet(viewsets.ViewSet):
    @action(detail=False, methods=["get"], url_path="pricing", authentication_classes=[])
    @method_decorator(csrf_exempt)  # For testing purposes only
    def pricing(self, request, pk=None):
        """ List all pricing or a specific server """

        try:
            server_type = request.query_params.get("server_type")
            location = request.query_params.get("location")

            # Case 1: returns the pricing list
            if not server_type or not location:
                pricing = self.hapi.pricing()
                return Response(pricing, status=status.HTTP_200_OK)

            # Case 2: returns the pricing of the provided server info
            prices = self.hapi.calculate_price(server_type, location)
            return Response(prices, status=status.HTTP_200_OK)

        except Exception as error:
            logger.critical(f"Error fetching server pricing: {error}", exc_info=True)
            return Response({"message": "Error fetching server pricing", "error": str(error)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

Я пытаюсь протестировать часть кода except Exception, но у меня возникают проблемы с вызовом исключения во время тестирования. Поскольку путь кода, ведущий к исключению, сложно воспроизвести в контролируемой среде тестирования, я не уверен, как эффективно протестировать логику обработки исключений.

Вот что я пробовал до сих пор:

def test_prices_exception(self, mocker, client):
    """ if pricing raised any exception """
    exception_error = "mock error"
    mocker.patch("core.views.datacenter.abc.abcVPS.pricing",
                 side_effect=Exception(exception_error))
    mock_logger = mocker.patch(
        "core.views.datacenter.abc.logger")

    url = SERVER_PRICE_URL

    response = client.get(url)

    assert "message" in response.data
    assert "error" in response.data
    assert mock_logger.critical.wrap_assert_called_once_with(
        f"Error fetching server pricing: {exception_error}")
    assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR

здесь я подражаю самому методу (pricing), что приводит к ошибке. и я также не могу пойти дальше, чтобы подражать чему-то внутри метода 'pricing'. или, может быть, я просто не знаю, как это сделать... нужно ли вообще тестировать что-то подобное?

  • Как проверить часть кода except Exception, если во время тестирования сложно вызвать исключение?
  • Есть ли у вас какие-либо советы или приемы для имитации условий, которые приведут к возникновению исключения в контролируемой среде тестирования?
  • Существуют ли альтернативные подходы или лучшие практики для тестирования обработки исключений в методах действий DRF, когда не представляется возможным вызвать исключение во время тестирования?

Любые советы или соображения по тестированию обработки исключений будут очень признательны. Большое спасибо!

Самое наивное решение: вы можете разделить это на два метода:

def pricing(self, request, pk=None):
    """ List all pricing or a specific server """
    try:
        self._pricing(request, pk)
    except Exception as error:
        logger.critical(f"Error fetching server pricing: {error}", exc_info=True)
        return Response({"message": "Error fetching server pricing", "error": str(error)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

def _pricing(self, request, pk):
    server_type = request.query_params.get("server_type")
    location = request.query_params.get("location")

    # Case 1: returns the pricing list
    if not server_type or not location:
        pricing = self.hapi.pricing()
        return Response(pricing, status=status.HTTP_200_OK)

    # Case 2: returns the pricing of the provided server info
    prices = self.hapi.calculate_price(server_type, location)
    return Response(prices, status=status.HTTP_200_OK)

Чтобы протестировать обработку исключений, вам нужно лишь подружить метод _pricing с побочным эффектом.

def test():
    with mock.patch("path.to._pricing", new=Mock(side_effect=Exception(exception_error))):
        response = client.get(url)
        ...
Вернуться на верх