如何在无法触发异常时测试异常处理?

1 投票
1 回答
37 浏览
提问于 2025-04-13 21:20

我正在做一个Django REST框架(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

在这里,我模拟了方法本身(定价),这会导致一个错误。而且我也不知道如何进一步模拟“定价”方法内部的内容。或者说我可能就是不知道怎么做……

这样测试真的有必要吗?

  • 在测试时很难引发异常的情况下,我该如何测试代码中的except Exception部分?
  • 你有什么技巧或方法可以模拟在受控测试环境中引发异常的条件吗?
  • 在测试DRF的动作方法时,如果无法在测试中触发异常,有没有其他方法或最佳实践来测试异常处理?

关于测试异常处理的任何建议或见解都将非常感激。谢谢!

1 个回答

1

最简单的解决办法是把这个问题分成两个方法来处理:

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)
        ...

撰写回答