使用Moto Mock EC2 Filter遇迭代错误,求解决建议
解决Mock EC2资源时的TypeError: 'Mock' object is not iterable问题
原Python类/函数
class A: def __init__(aws_profile_name, aws_region, ec2_id): self.session = boto3.session.Session(profile_name=aws_profile_name, aws_region) self.ec2 = EC2(self.session, aws_region, id=ec2_id) class EC2: def __init__(self, session, aws_region, id): self.session = session self.region = aws_region self.id = id self.ec2_resource = self.session.resource("ec2", region_name=self.region) self.ec2_client = self.session.client("ec2", region_name=self.region) self.instance = self.filter_ec2_by_id() def filter_ec2_by_id(self): return self.filter( InstanceIds=[ self.get_instance_id(), ] ) def filter(self, kwargs): instances = self.ec2_resource.instances.filter(**kwargs) instance_list = [instance for instance in instances] return instance_list
测试代码(test.py)
from unittest import mock import sys from moto import mock_ec2 import boto3 @mock_ec2 def test_mock_session(): mock_session_obj = mock.Mock() ec2 = boto3.resource("ec2", region_name="us-east-1") reservation = client.run_instances(ImageId="ami-1234", MinCount=2, MaxCount=2) instance1 = ec2.Instance(reservation["Instances"][0]["InstanceId"]) ec2 = EC2(mock_session_obj, region="us-east-1", id=instance1) test_mock_session()
错误信息
instance_list = [instance for instance in instances] TypeError: 'Mock' object is not iterable
问题根源
你用mock.Mock()创建的mock_session_obj,其resource()方法默认返回Mock对象,而该Mock的instances.filter()返回的仍是不可迭代的Mock,导致遍历时报错。同时原类和测试代码还存在几处语法/逻辑遗漏问题。
解决建议
方案1:正确Mock调用链,返回可迭代对象
手动Mocksession.resource()的返回值,让instances.filter()返回可迭代的模拟实例列表,同时补充未定义的方法:
from unittest import mock import sys from moto import mock_ec2 import boto3 # 替换为EC2类所在的实际模块 from your_module import EC2 @mock_ec2 def test_mock_session(): mock_session_obj = mock.Mock() # 模拟EC2资源对象 mock_ec2_resource = mock.Mock() # 模拟单个EC2实例 mock_instance = mock.Mock() # 让filter返回包含模拟实例的可迭代列表 mock_ec2_resource.instances.filter.return_value = [mock_instance] # 绑定session.resource的返回值 mock_session_obj.resource.return_value = mock_ec2_resource # Mock EC2类中未定义的get_instance_id方法 with mock.patch.object(EC2, 'get_instance_id', return_value='i-123456'): ec2 = EC2(mock_session_obj, aws_region="us-east-1", id='i-123456') # 断言返回的实例列表长度正确 assert len(ec2.instance) == 1 test_mock_session()
方案2:利用moto模拟真实Session(更推荐)
既然已经用moto模拟EC2服务,直接使用moto创建的真实Session传递给EC2类,测试更贴近真实场景:
第一步:修正原类的语法错误
import boto3 class A: # 补充self参数,修正Session的region参数名 def __init__(self, aws_profile_name, aws_region, ec2_id): self.session = boto3.session.Session(profile_name=aws_profile_name, region_name=aws_region) self.ec2 = EC2(self.session, aws_region, id=ec2_id) class EC2: def __init__(self, session, aws_region, id): self.session = session self.region = aws_region self.id = id self.ec2_resource = self.session.resource("ec2", region_name=self.region) self.ec2_client = self.session.client("ec2", region_name=self.region) self.instance = self.filter_ec2_by_id() # 补充未定义的get_instance_id方法 def get_instance_id(self): return self.id def filter_ec2_by_id(self): return self.filter(InstanceIds=[self.get_instance_id()]) def filter(self, kwargs): instances = self.ec2_resource.instances.filter(**kwargs) instance_list = [instance for instance in instances] return instance_list
第二步:修改测试代码
from moto import mock_ec2 import boto3 # 替换为EC2类所在的实际模块 from your_module import EC2 @mock_ec2 def test_mock_session(): # 使用moto创建真实Session session = boto3.session.Session(region_name="us-east-1") ec2_client = session.client("ec2") # 创建测试用EC2实例 reservation = ec2_client.run_instances(ImageId="ami-1234", MinCount=2, MaxCount=2) instance_id = reservation["Instances"][0]["InstanceId"] # 初始化EC2类,传递真实Session和实例ID ec2 = EC2(session, aws_region="us-east-1", id=instance_id) # 断言筛选出的实例数量正确 assert len(ec2.instance) == 1 # 断言实例ID匹配 assert ec2.instance[0].id == instance_id test_mock_session()
内容的提问来源于stack exchange,提问作者spiderloop
相关产品推荐
相关产品推荐

