Python3中如何mock配置变量实现无需AWS凭证运行单元测试?
问题根因
报错的核心原因是模块导入阶段就已经触发了AWS SSM调用:你在测试代码顶部直接写from service import send_mail时,Python会先执行service.py的导入逻辑,加载configs模块,而configs.py里的load_parameters是模块级顶层代码,会在导入时直接执行,这时候你的mock逻辑还没有运行,自然会触发AWS凭证校验报错。你后续mockservice.SENDER这类变量的时候,SSM调用已经执行完成,所以mock不会生效。
解决方案
方案1:无需修改业务代码,调整测试逻辑即可快速解决
调整测试代码的导入顺序,先mock掉SSM参数加载逻辑,再导入业务模块:
# test_service.py from unittest import TestCase, main, mock class TestMailService(TestCase): def test_service(self): # 第一步:先mock configs模块的SSM参数加载方法 with mock.patch('configs.load_parameters') as mock_load: # 给mock配置不同参数对应的返回值 mock_load.side_effect = lambda param_name: { # 填入你实际参数路径对应的测试返回值 "/your/path/mail_sender": "abc@sys.com", "/your/path/mail_destinations": "def@sys.com", # 其他configs中需要加载的参数都可以在这里配置返回值 }.get(param_name, "mock_default_value") # 第二步:mock完成后再导入service模块,此时导入不会触发真实SSM调用 from service import send_mail with mock.patch('service.logging.Logger.info') as mocked_logging: send_mail(...) mocked_logging.assert_called_with('mail sent Successfully') if __name__ == "__main__": main()
如果有多个测试用例需要复用mock,可以把mock逻辑放到setUp()方法中提前执行即可。
方案2:重构配置加载逻辑,从根源提升可测试性(长期最优方案)
把configs.py的模块级参数加载逻辑改成懒加载,避免导入时直接触发SSM调用:
# configs.py 重构后 from handlers.ssm_handler import load_parameters from common import constants import os environment = os.environ.get(constants.ENVIRONMENT) # 改为调用时才加载参数 def get_config(config_key): param_path_map = { "JSON_BUCKET": constants.OT_ARCHIVAL_PREFIX+environment+constants.MIGRATION_BUCKET, "SENDER": constants.OT_ARCHIVAL_PREFIX+environment+constants.MAIL_SENDER, "DESTINATIONS": constants.OT_ARCHIVAL_PREFIX+environment+constants.MAIL_DESTINATIONS, } return load_parameters(param_path_map[config_key])
调整service.py的参数获取方式:
#service.py 重构后 from configs import get_config from constants import LOG_FORMAT import logging def send_mail(body): SENDER = get_config("SENDER") DESTINATIONS = get_config("DESTINATIONS") ...
调整后测试代码可以直接在顶部导入业务模块,只需要mockget_config方法即可,不需要处理导入顺序:
# 重构后的test_service.py from unittest import TestCase, main, mock from service import send_mail class TestMailService(TestCase): @mock.patch('service.get_config') @mock.patch('service.logging.Logger.info') def test_service(self, mocked_logging, mock_get_config): # 配置get_config的返回值 mock_get_config.side_effect = lambda key: { "SENDER": "abc@sys.com", "DESTINATIONS": "def@sys.com" }.get(key) send_mail(...) mocked_logging.assert_called_with('mail sent Successfully')
内容的提问来源于stack exchange,提问作者ahkam
相关产品推荐
相关产品推荐

