Ошибка AssertionError: expected call not found при запуске тестов библиотеки python unittest

Написал тест для функции с вложенными функциями. Но при запуске тестов появляется ошибка.

Тестируемые функции:

Файл google_drive.uploader.py

def load_file_to_disk(file_path: str, name: str, folder_id: str) -> None:
    """Подключается к Google Drive и загружает файл на диск, используя путь к
    файлу, название и id папки(куда нужно загрузить файл), взятые из
    аргументов."""

    max_retries = 3
    attempt = 0
    success = False
    while attempt < max_retries and not success:
        try:
            if check_file_exist(name, folder_id):
                try:
                    file_id = get_id_object_by_name(name, folder_id)
                    result = update_file(file_path, file_id)
                    if result['id']:
                        success = True
                        logger.debug(f"Файл {name} обновлен в папке "
                                     f"'{folder_id}'.")
                except Exception as e:
                    logger.error(f'Ошибка при обновлении файла {name}: {e}')
            else:
                result = create_file(name, folder_id, file_path)
                if result['id']:
                    success = True
                    logger.debug(f"Файл {name} загружен в папку "
                                 f"'{folder_id}'.")
        except Exception as e:
            attempt += 1
            logger.error(f"Ошибка загрузки/обновления файла на Google Drive. "
                         f"Попыток: {attempt}. Ошибка: {e}")
            if attempt < max_retries:
                time.sleep(2)
    if not success:
        send_mail(f"Ошибка загрузки файла {name} в папку с id '{folder_id}' "
                  f"после {max_retries} attempts.")
        logger.error(f"Ошибка загрузки файла {name} в папку с id "
                     f"'{folder_id}'.")


def check_file_exist(name: str, parent_folder_id: str) -> bool:
    """Получает все файлы с гугл диска, которые удовлетворяют условия запроса:
    файлы из папки с id 'parent_folder_id' и с именами 'main_log.log или
    'widget_log_bd'."""

    service = auth_gdrive()

    query = (f"'{parent_folder_id}' in parents and name = '{name}'")

    results = service.files().list(
        pageSize=10,
        fields="nextPageToken, files(id, name, mimeType)",
        q=query).execute()
    logger.debug(f"Выполнена проверка на существование файла "
                 f"'{name}' в папке с id "
                 f"'{parent_folder_id}'.")
    if not results["files"]:
        logger.debug(f"Файл '{name}' отсутствуtт.")
        return False
    logger.debug(f"Файл '{name}' найден.")
    return True


def get_id_object_by_name(name: str, folder_id: str = None) -> str:
    """Подключается к Google Drive и получает id папки или файла, по названию,
    переданному в аргументе и возвращает его в виде {название: id}."""

    service = auth_gdrive()
    if folder_id:
        query = f"name = '{name}' and '{folder_id}' in parents"
    else:
        query = f"name = '{name}'"
    r = service.files().list(pageSize=10,
                             fields="nextPageToken, files(id, name, mimeType)",
                             q=query).execute()
    if r["files"]:
        logger.debug(f"Получен id файла {name} в папке с id '{folder_id}'.")
        return r["files"][0]["id"]

    else:
        logger.debug(f"id файла {name} в папке {folder_id} не найден. "
                     f"Проверьте наличие файла в папке с id {folder_id} '.")


def update_file(file_path: str, file_id: str) -> Dict[str, str]:
    """Обновляет файл на гугл-диске с помощью пути и id файла."""

    service = auth_gdrive()
    media = MediaFileUpload(file_path, resumable=True)
    return service.files().update(
            fileId=file_id,
            media_body=media).execute()


def create_file(name: str, folder_id: str, file_path: str) -> Dict[str, str]:
    """Загружает файл на Google Drive, используя имя, id родительской папки, в
    которой нужно создать, и путь к файлу."""

    service = auth_gdrive()
    file_metadata = {
                        "name": name,
                        "parents": [folder_id]
            }
    media = MediaFileUpload(file_path, resumable=True)
    return service.files().create(
                    body=file_metadata,
                    media_body=media,
                    fields="id,name"
                ).execute()

Файл tests.py

@patch('google_drive.uploader.check_file_exist')
    @patch('google_drive.uploader.get_id_object_by_name')
    @patch('google_drive.uploader.update_file')
    @patch('google_drive.uploader.create_file')
    def test_load_file_to_disk(self, mock_check_file, mock_get_id,
                               mock_update_file, mock_create_file):
        mock_response_check = MagicMock()
        response_dict_check = True
        mock_response_check.json.return_value = response_dict_check
        mock_check_file.return_value = mock_response_check

        mock_response_get = MagicMock()
        response_dict_get = '18Wwvuye8dOjCZfJzGf45yQvB87Lazbzu'
        mock_response_get.json.return_value = response_dict_get
        mock_get_id.return_value = mock_response_check

        mock_response_update = MagicMock()
        response_dict_update = {'kind': 'drive#file',
                                'id': '18Wwvuye8dOjCZfJzGf45yQvB87Lazbzu',
                                'name': 'main_log_i-analytic6.pz.cp.log',
                                'mimeType': 'text/plain'
                                }
        mock_response_update.json.return_value = response_dict_update
        mock_update_file.return_value = mock_response_check

        mock_response_create = MagicMock()
        response_dict_create = {'id': '18Wwvuye8dOjCZfJzGf45yQvB87Lazbzu'}
        mock_response_create.json.return_value = response_dict_create
        mock_create_file.return_value = mock_response_check

        result = load_file_to_disk('file_path', 'name', 'folder_id')

        print(mock_check_file.call_args_list)  # Отладочный вывод
        print(mock_get_id.call_args_list)
        print(mock_update_file.call_args_list)
        print(mock_create_file.call_args_list)

        mock_check_file.assert_called_with('name', 'parent_folder_id')
        mock_get_id.assert_called_with('name', 'folder_id')
        mock_update_file.assert_called_with('file_path', 'file_id')
        mock_create_file.assert_not_called()
        self.assertEqual(result, None)


if __name__ == "__main__":
    unittest.main()

Ошибка:

python -m unittest
DEBUG:google_drive.uploader:Запуск автозагрузчика...
DEBUG:google_drive.uploader:Получен id файла main_log_i-analytic6.pz.cp.log в папке с id '1DvWKXFKjddttDIWrqyKo6DqTgpv6rfFH'.
17OaoGOZkIUhRP658nXMHYshSDg6KByzX
{'kind': 'drive#file', 'id': '17OaoGOZkIUhRP658nXMHYshSDg6KByzX', 'name': 'main_log_i-analytic6.pz.cp.log', 'mimeType': 'text/plain'}
INFO:google_drive.uploader:Создана папка 20_12_2024.
.DEBUG:google_drive.uploader:Получен id файла exist_file в папке с id 'test_folder'.
.DEBUG:google_drive.uploader:id файла nonexistent-file в папке test_folder не найден. Проверьте наличие файла в папке с id test_folder '.
.DEBUG:google_drive.uploader:Файл name обновлен в папке 'folder_id'.
[]
[call('file_path', <MagicMock name='create_file()' id='2765363568928'>)]
[call('name', 'folder_id')]
[call('name', 'folder_id')]
F
======================================================================
FAIL: test_load_file_to_disk (tests.TestUploader.test_load_file_to_disk)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\DOrlov\AppData\Local\Programs\Python\Python313\Lib\unittest\mock.py", line 1424, in patched
    return func(*newargs, **newkeywargs)
  File "C:\Users\DOrlov\Рабочая папка\Projects\admin-tools\tests.py", line 102, in test_load_file_to_disk
    mock_check_file.assert_called_with('name', 'parent_folder_id')
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\DOrlov\AppData\Local\Programs\Python\Python313\Lib\unittest\mock.py", line 968, in assert_called_with
    raise AssertionError(error_message)
AssertionError: expected call not found.
Expected: create_file('name', 'parent_folder_id')
  Actual: not called.

----------------------------------------------------------------------
Ran 4 tests in 0.020s

FAILED (failures=1)

Предполагаю, что ошибка связана с

mock_check_file.assert_called_with('name', 'parent_folder_id')
mock_get_id.assert_called_with('name', 'folder_id')
mock_update_file.assert_called_with('file_path', 'file_id')
mock_create_file.assert_not_called()

Так как, закомментировав этот участок кода, тесты проходят, но по принтам можно сказать, что есть какие-то проблемы:

$ python -m unittest tests.TestUploader.test_load_file_to_disk
DEBUG:google_drive.uploader:Запуск автозагрузчика...
DEBUG:google_drive.uploader:Файл name обновлен в папке 'folder_id'.
[]
[call('file_path', <MagicMock name='create_file()' id='2799013179952'>)]
[call('name', 'folder_id')]
[call('name', 'folder_id')]
F
======================================================================
FAIL: test_load_file_to_disk (tests.TestUploader.test_load_file_to_disk)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\DOrlov\AppData\Local\Programs\Python\Python313\Lib\unittest\mock.py", line 1424, in patched
    return func(*newargs, **newkeywargs)
  File "C:\Users\DOrlov\Рабочая папка\Projects\admin-tools\tests.py", line 102, in test_load_file_to_disk
    mock_check_file.assert_called_with(name='name', parent_folder='parent_folder_id')
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\DOrlov\AppData\Local\Programs\Python\Python313\Lib\unittest\mock.py", line 968, in assert_called_with
    raise AssertionError(error_message)
AssertionError: expected call not found.
Expected: create_file(name='name', parent_folder='parent_folder_id')
  Actual: not called.

----------------------------------------------------------------------
Ran 1 test in 0.005s

FAILED (failures=1)

Ответы (1 шт):

Автор решения: Pak Uula

У вас в тесте противоречие. Вы одновременно желаете и не желаете, чтобы функция create_file была вызвана:

        mock_check_file.assert_called_with('name', 'parent_folder_id')
        ...
        mock_create_file.assert_not_called()

Какой-то из этих ассертов обязательно сломается.

Судя по логике заглушек, правильным является последняя проверка - метод create_file не должен вызываться. Смотрите, как вы настраиваете заглушку для check_file_exist:

        mock_response_check = MagicMock()
        response_dict_check = True
        mock_response_check.json.return_value = response_dict_check

Эта функция должна в тесте всегда возвращать True. Следовательно тестируемая функция должна всегда заходить в ветку if с update_file. В этой ветке нет create_file, и ассерт mock_check_file.assert_called_with должен нарушаться.

Если вам нужен тест, в котором create_file вызывается, то замените response_dict_check = True на ... = False и поменяйте ассерты:

mock_get_id.assert_called_with('name', 'folder_id')
mock_update_file.assert_not_called()
mock_check_file.assert_called_with('name', 'parent_folder_id')
→ Ссылка