使用 mock.patch() 时出现 ModuleNotFoundError

ModuleNotFoundError when using mock.patch()

我想模拟来自 Python Kubernetes 客户端的响应。下面是我的 Kubernetes 服务的代码:

import os

from kubernetes.client.rest import ApiException
from kubernetes import client
from kubernetes.config import load_config
from exceptions.logs_not_found_exceptions import LogsNotFound
import logging

log = logging.getLogger("services/kubernetes_service.py")


class KubernetesService:
    def __init__(self):
        super().__init__()
        if os.getenv("DISABLE_KUBERNETES_CONFIG") == "False":
            load_config()

        self.api_instance = client.CoreV1Api()

    def get_namespaces(self):
        try:
            api_response = self.api_instance.list_namespace()
            dict_response = api_response.to_dict()

            namespaces = []
            for item in dict_response['items']:
                namespaces.append(item['metadata']['name'])

            log.info(f"Retrieved the namespaces: {namespaces}")
            return namespaces
        except ApiException as e:
            raise e

当我想使用 mock.patch 模拟这个时,我收到了 ModuleNotFoundError。下面是我的测试代码 class

import os
from unittest import mock
from tests.unit_tests import utils
from services.kubernetes_service import KubernetesService

class TestKubernetesService:
    @mock.patch.dict(os.environ, {"DISABLE_KUBERNETES_CONFIG": "True"})
    def test_get_namespaces(self):
        self.service = KubernetesService()
        print(self.service.api_instance)
        with mock.patch('services.kubernetes_service.KubernetesService.api_instance.list_namespace',
                        return_value=utils.kubernetes_namespaces_response()):
            actual_result = self.service.get_namespaces()
            assert actual_result == ['default', 'kube-node-lease', 'kube-public', 'kube-system']

当我将 mock.patch 中的路径从 services.kubernetes_service.KubernetesService.api_instance.list_namespace 编辑到 services.kubernetes_service.KubernetesService.get_namespaces 时,它成功模拟了我输入的 return 值。但我想模拟的响应KubernetesService class.

中的行 self.api_instance.list_namespace()

有人有想法吗?

在您的示例中,您尝试修补 class (api_instance) 的实例属性。这不能通过仅从 class 引用它来完成,因为它不是 class 属性 - 您需要一个实例。

通常有两种标准方法来模拟实例属性:

  • mock 整个 class,在这种情况下,mock 的 class 上的 return_value 属性将是一个替换 class 的任何实例的 mock,因此可以用于模拟实例属性
  • 使用 mock.patch.object 或类似方法模拟具体实例 - 这要求您可以访问测试中的实例

模拟整个 class 在您的情况下不是一个选项,因为您需要使用 class 的功能,但是因为您可以通过 self.service 访问实例, 您可以使用 patch.object:

    def test_get_namespaces(self):
        self.service = KubernetesService()
        with mock.patch.object(self.service.api_instance, 'list_namespace', 
                               return_value=utils.kubernetes_namespaces_response()):
            actual_result = self.service.get_namespaces()
            assert actual_result == ['default', 'kube-node-lease', 'kube-public', 'kube-system']