You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行Mock测试调用ListAccessKeys时遭遇ExpiredToken错误求解决方案

解决模块级boto3客户端的测试桩问题(不修改原代码)

你遇到的核心问题是:handler.py在模块加载阶段就初始化了iam_client,此时测试的桩逻辑还未生效,导致代码调用了真实的AWS IAM客户端,触发令牌过期错误。以下是无需修改handler.py的解决方案:

方案1:在导入handler前完成boto3打桩

将boto3的桩逻辑移到导入handler的代码块内,确保模块初始化时使用的是桩客户端而非真实客户端:

import sys
sys.path.insert(1, 'folder1/folder2/folder3')
import unittest
import boto3
from datetime import datetime
from botocore.stub import Stubber
from unittest import mock
from unittest.mock import patch, MagicMock

# 先配置环境变量,同时打桩boto3.client
with mock.patch.dict('os.environ',{
    'ENVIRONMENT': 'us-east-1',
    'NOTIFICATION_LAMBDA': '',
    'NOTIFICATION_SENDER': '',
    'PLATFORM_CONTACT': '',
    'KEY_ROTATION_DL_TAG_NAME': ''
}), patch('boto3.client') as mock_boto_client:
    # 创建IAM桩客户端并关联Stubber
    iam_client = boto3.client('iam')
    stubber = Stubber(iam_client)
    mock_boto_client.return_value = iam_client
    
    # 模拟handler中未初始化的secretmanager
    mock_secretmanager = MagicMock()
    mock_boto_client.side_effect = lambda service: mock_secretmanager if service == 'secretsmanager' else iam_client
    
    # 此时导入handler,模块级的iam_client会使用我们的桩
    import handler

class TestLambda(unittest.TestCase):
    def test_create_key(self):
        # 定义list_access_keys的桩响应
        expected_list_response = {
            'AccessKeyMetadata': [
                {
                    'UserName': 'testUser',
                    'AccessKeyId': 'AKIA111111111EXAMPLE',
                    'Status': 'Active',
                    'CreateDate': datetime(2015, 1, 1)
                }
            ]                                
        }
        # 定义create_access_key的桩响应
        expected_create_response = {
            'AccessKey': {
                'AccessKeyId': 'AKIA222222222EXAMPLE',
                'SecretAccessKey': 'secret123'
            }
        }
        
        # 给Stubber添加预期调用
        stubber.add_response('list_access_keys', expected_list_response, {'UserName': 'testUser'})
        stubber.add_response('create_access_key', expected_create_response, {'UserName': 'testUser'})
        
        # 激活Stubber并执行测试
        with stubber:
            result = handler.create_key('testUser')
            
            # 验证返回值符合预期
            self.assertEqual(result[0], "OK")
            self.assertIn("New Key created for IAM user 'testUser'", result[1]['subject'])
            self.assertIn("AKIA222222222EXAMPLE", result[1]['body'])
            
            # 验证secretmanager的方法调用
            handler.secretmanager.describe_secret.assert_called_once_with(SecretId='testUser')
            handler.secretmanager.create_secret.assert_called_once_with(Name='testUser')

方案2:直接替换handler模块的iam_client实例

如果不想提前打桩,也可以在测试方法中直接替换已初始化的handler.iam_client:

import sys
sys.path.insert(1, 'folder1/folder2/folder3')
import unittest
import boto3
from datetime import datetime
from botocore.stub import Stubber
from unittest import mock
from unittest.mock import MagicMock

with mock.patch.dict('os.environ',{
    'ENVIRONMENT': 'us-east-1',
    'NOTIFICATION_LAMBDA': '',
    'NOTIFICATION_SENDER': '',
    'PLATFORM_CONTACT': '',
    'KEY_ROTATION_DL_TAG_NAME': ''
}):
    import handler

class TestLambda(unittest.TestCase):
    def test_create_key(self):
        # 创建IAM桩客户端和Stubber
        iam_client = boto3.client('iam')
        stubber = Stubber(iam_client)
        
        # 定义桩响应
        expected_list_response = {
            'AccessKeyMetadata': [
                {
                    'UserName': 'testUser',
                    'AccessKeyId': 'AKIA111111111EXAMPLE',
                    'Status': 'Active',
                    'CreateDate': datetime(2015, 1, 1)
                }
            ]                                
        }
        expected_create_response = {
            'AccessKey': {
                'AccessKeyId': 'AKIA222222222EXAMPLE',
                'SecretAccessKey': 'secret123'
            }
        }
        
        # 添加预期调用
        stubber.add_response('list_access_keys', expected_list_response, {'UserName': 'testUser'})
        stubber.add_response('create_access_key', expected_create_response, {'UserName': 'testUser'})
        
        # 替换handler里的iam_client和未定义的secretmanager
        handler.iam_client = iam_client
        handler.secretmanager = MagicMock()
        
        with stubber:
            result = handler.create_key('testUser')
            
            # 验证结果
            self.assertEqual(result[0], "OK")
            self.assertIn("AKIA222222222EXAMPLE", result[1]['body'])

关键修改说明

  1. 提前打桩boto3:确保handler.py初始化iam_client时使用的是桩客户端,避免调用真实AWS服务。
  2. 模拟secretmanager:原handler代码遗漏了secretmanager的初始化逻辑,测试中用MagicMock模拟该对象,避免报错。
  3. 修正断言逻辑:原测试错误地断言create_key返回值等于list_access_keys的响应,实际create_key返回的是("OK", {"subject": ..., "body": ...}),需调整断言匹配实际返回结构。

内容的提问来源于stack exchange,提问作者RunRabbit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 01:50:02