【问题标题】:How to mock config variables in python3?如何在 python3 中模拟配置变量?
【发布时间】:2021-11-20 09:14:23
【问题描述】:

这是一个 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 凭证。 作为解决方案,我尝试模拟 SENDER 和 DESTINATIONS。但它给我带来了错误(期待安全令牌)

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

【问题讨论】:

    标签: python amazon-web-services python-unittest python-unittest.mock


    【解决方案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),
        }
    

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

    解决方案 2

    当还没有活动的模拟/补丁时,不要导入任何会反过来导入 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(...)
    
    

    【讨论】:

    • 对于解决方案 2,我们不需要类吗?
    • 对于解决方案 2,您可以尝试使用类和选项 3。我没有尝试过选项 1 或 2 是否可以开箱即用,您可能需要更多设置。
    • 我在课堂上尝试了选项 3。它不工作
    • 该错误是否仍与缺少 AWS 凭证有关?如果是,那么configs.py 在修补之前仍然被执行。检查是否有任何可能导致导入 configs.py 的先前导入。另外,验证我制作的补丁是否是正确的路径'handlers.ssm_handler.load_parameters',因为我只是基于问题。
    猜你喜欢
    • 2021-09-10
    • 2011-01-25
    • 1970-01-01
    • 2019-06-29
    • 1970-01-01
    • 2023-02-15
    • 2017-01-16
    • 2021-10-09
    • 1970-01-01
    相关资源
    最近更新 更多