模拟AWS资源抛出ClientError时DynamoDB单元测试失效问题求解
问题原因
- 第一,Stubber绑定的客户端实例不匹配。你在测试类
setUp中初始化的self.dynamodb资源实例,和foo.py的update_table函数内部重新调用boto3.resource('dynamodb')生成的资源实例是两个完全独立的对象,你给前者客户端绑定的Stubber完全不会作用到后者的调用上。 - 第二,
@mock_dynamodb2装饰器优先级高于Stubber。moto提供的这个装饰器会全局接管所有boto3的DynamoDB请求,所有相关操作都会直接走moto的模拟实现,不会触发你手动添加的Stubber错误。
解决方案
这里提供两种可行的实现方式:
方案1:使用Stubber模拟,移除moto的DynamoDB mock
不需要依赖moto的模拟表,直接通过Stubber绑定调用错误,修改点如下:
- 去掉测试类上的
@mock_dynamodb2装饰器,删除setUp中创建模拟表的相关代码(Stubber不需要真实模拟表存在) - 调整测试逻辑让Stubber作用到被测函数内部生成的dynamodb实例上,参考代码:
import boto3 import botocore from botocore.stub import Stubber import foo import unittest from unittest import mock class TestUpdateTable(unittest.TestCase): def test_returns_none(self): stubber = None original_resource = boto3.resource def mock_resource(service): nonlocal stubber if service == 'dynamodb': dynamodb = original_resource('dynamodb') stubber = Stubber(dynamodb.meta.client) stubber.add_client_error('update_item') stubber.activate() return dynamodb return original_resource(service) with mock.patch('foo.boto3.resource', side_effect=mock_resource): response = foo.update_table("123") stubber.deactivate() self.assertIsNone(response)
方案2:保留moto mock,直接mock update_item方法抛出异常
如果你需要保留moto的DynamoDB模拟做其他测试,可以直接模拟Table对象的update_item方法抛出ClientError,代码更简洁:
# 保留原测试类的@mock_dynamodb2装饰器和setUp代码 def test_returns_none(self): with mock.patch('foo.boto3.resource') as mock_resource: # 配置mock的table对象的update_item方法抛出ClientError mock_table = mock_resource.return_value.Table.return_value mock_table.update_item.side_effect = botocore.exceptions.ClientError( {'Error': {'Code': 'InternalError', 'Message': 'DynamoDB internal error'}}, 'UpdateItem' ) response = foo.update_table("123") self.assertIsNone(response)
内容的提问来源于stack exchange,提问作者Connor
相关产品推荐
相关产品推荐

