简体   繁体   中英

ModuleNotFoundError when using mock.patch()

I want to mock a response from the Python Kubernetes client. Below code of my Kubernetes service:

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

When I want to mock this using mock.patch I'm getting a ModuleNotFoundError. Below code of my Test 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']

When I edit the path in mock.patch from services.kubernetes_service.KubernetesService.api_instance.list_namespace to services.kubernetes_service.KubernetesService.get_namespaces it successfully mocks the return value I put in. But I want to mock the response of the line self.api_instance.list_namespace() in the KubernetesService class.

Someone an idea?

In your example, you try to patch an instance attribute of the class ( api_instance ). This cannot be done by just referencing it from the class, as it is not a class attribute - you need an instance instead.

There are generally two standard methods to mock an instance attribute:

  • mock the whole class, in which case the return_value attribute on the mocked class will be a mock that replaces any instance of the class and can therefore be used for mocking instance attributes
  • mock a concrete instance using mock.patch.object or similar - this requires that you have access to the instance in your test

Mocking the whole class is not an option in your case, as you need to use the functionality of the class, but as you have access to the instance via self.service , you can use 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']

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM