Как имитировать переменные конфигурации в python3?

#python #amazon-web-services #python-unittest #python-unittest.mock

Вопрос:

Это функция AWS lambda

 #service.py
from configs import SENDER, DESTINATIONS
from constants import LOG_FORMAT
import logging

def send_mail(body, SENDER, DESTINATIONS):
    ...
    ...
 

В файлах конфигурации он извлекает данные из хранилища параметров AWS

 # configs.py
from handlers.ssm_handler import load_parameters
from common import constants
import os
environment = os.environ.get(constants.ENVIRONMENT)

JSON_BUCKET = load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MIGRATION_BUCKET)
SENDER = load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_SENDER)
DESTINATIONS = load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_DESTINATIONS)
...
 

Поэтому, когда я пытаюсь это проверить

 # test_service.py
from unittest import TestCase, main, mock
from service import send_mail

class TestMailService(TestCase):
     def test_service(self):
       with mock.patch('service.SENDER', 'abc@sys.com') as mocked_sender:
         with mock.patch('service.DESTINATIONS', 'def@sys.com') as mocked_sender:
           with mock.patch('service.logging.Logger.info') as mocked_logging:
              send_mail(...)
              mocked_logging.assert_called_with('mail sent Successfully')
 

Этот тестовый случай проходит, когда я экспортирую учетные данные безопасности AWS. Но это произойдет, если я не сдам документы. Я думаю, это потому, что в service.py файл он открывает весь config.py файл. Поэтому для вызова AWS потребуются учетные данные sec.
В качестве решения я попытался высмеять ОТПРАВИТЕЛЯ и АДРЕСАТОВ. Но это выдает мне ошибку(ожидание токенов безопасности)

Я хочу, чтобы unittest был независимым от токена безопасности. Предложите решение

Ответ №1:

Это происходит потому, что при импорте configs.py например , через from configs import SENDER, DESTINATION , он автоматически запускал бы те операторы, которые вызывают load_parameters , которые, в свою очередь, вызывают AWS SSM, даже если еще нет активных насмешек/исправлений.

Решение 1

Попробуйте провести рефакторинг configs.py таким образом, установка переменных будет происходить только при явном вызове (а не при импорте). Простейшей реализацией было бы что-то вроде:

configs.py

 import os

from common import constants
from handlers.ssm_handler import load_parameters


def get_params():
    environment = os.environ.get(constants.ENVIRONMENT)
    return {
        "SENDER": load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_SENDER),
        "DESTINATIONS": load_parameters(constants.OT_ARCHIVAL_PREFIX environment constants.MAIL_DESTINATIONS),
    }
 

Для этого потребуется некоторый рефакторинг, так как вызов get_params должен быть вставлен в начале вызова функции AWS Lambda. Таким образом, вызов load_parameters , который, в свою очередь, использует AWS SSM, не будет выполняться автоматически, и мы сможем подготовить наши насмешки/исправления до его вызова.

Решение 2

Не импортируйте ни один файл, который, в свою очередь, импортировал бы configs.py пока еще нет активного макета/патча. load_parameters Сначала исправьте, чтобы он не подключался к фактической SSM AWS. Вы можете исправить это вручную или использовать декоратор @mock_ssm из moto. Только тогда мы сможем безопасно импортировать файлы.

 from unittest import TestCase, main, mock

from moto import mock_ssm

# from service import send_mail  # REMOVE THIS IMPORT!


@mock_ssm  # Option 1. Requires <pip install moto>. You have to setup SSM first as usual.
def test_service(mocker):  # Requires <pip install pytest-mock>
    mocker.patch('handlers.ssm_handler.load_parameters')  # Option 2
    # with mock.patch('handlers.ssm_handler.load_parameters') as mock_ssm:  # Option 3. This is equivalent to Option 2.

    mocker.patch('service.SENDER', 'abc@sys.com')
    mocker.patch('service.DESTINATIONS', 'def@sys.com')

    from service import send_mail  # Import it here after the patches have taken effect
    send_mail(...)

 

Комментарии:

1. Для решения 2 разве нам не нужен класс?

2. Для решения 2 вы можете попробовать использовать классы вместе с вариантом 3. Я не пробовал, будет ли вариант 1 или 2 работать из коробки, вам может потребоваться дополнительная настройка.

3. Я попробовал вариант 3 с классом. это не работает

4. По-прежнему ли ошибка связана с отсутствием учетных данных AWS? Если это так, то configs.py все равно было выполнено до исправления. Проверьте, есть ли какие-либо предыдущие импортные операции, которые могут привести к импорту configs.py . Кроме того, проверьте, является ли исправление, которое я сделал, правильным путем 'handlers.ssm_handler.load_parameters' , поскольку я основывал его только на вопросе.