如何模拟 python3 中的配置变量?

How to mock config variables in python3?

这是一个 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 文件。因此,它需要 sec 凭据才能调用 AWS。 作为解决方案,我尝试模拟 SENDER 和 DESTINATIONS。但它抛出我错误(期待安全令牌)

我希望单元测试独立于安全令牌。提出解决方案

发生这种情况是因为当您导入 configs.py 例如通过 from configs import SENDER, DESTINATION,它会自动 运行 那些调用 load_parameters 的语句,即使没有活动的 mocks/patches 又会调用 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),
    }

这需要进行一些重构,因为必须在 AWS Lambda 函数调用的开头插入对 get_params 的调用。这样,使用 AWS SSM 的 load_parameters 的调用将不会自动执行,我们可以在调用之前准备好 mocks/patches。

解决方案 2

当还没有活动的 mock/patch 时,不要导入任何会依次导入 configs.py 的文件。首先修补 load_parameters,这样它就不会连接到实际的 AWS SSM。您可以手动修补它,也可以使用 moto 中的装饰器 @mock_ssm。只有这样我们才能安全地导入文件。

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