单元测试读取S3配置文件返回列表时MagicMock异常排查
问题分析与解决方案
核心问题
你的单元测试返回MagicMock对象而非预期值,导致断言错误,主要有三个原因:
- 参数传递错误:原函数的
configparser参数接收的是模块对象,但测试中传入的是ConfigParser的Mock实例,导致函数内configparser.ConfigParser()调用的是Mock实例的属性,而非模块的类构造方法。 - 模拟方式不匹配:原函数使用
config[section][key]的索引语法获取值,但测试中模拟的是get()方法的返回值,两者没有对应关系。 - 函数内硬编码实例化:原函数内直接调用
configparser.ConfigParser()创建实例,测试需要正确模拟这个类的实例行为。
修正后的测试代码
import unittest from unittest import mock # 替换为原函数所在的模块名 from your_module import read_config @mock.patch('boto3.client') class TestFunctions(unittest.TestCase): @mock.patch('configparser.ConfigParser') def test_read_config(self, mock_config_parser_cls, mock_boto3_client): # 模拟S3客户端与返回的文件内容 mock_s3_client = mock.Mock() mock_boto3_client.return_value = mock_s3_client mock_body = mock.Mock() mock_body.read.return_value = b""" [idx_list] idx1 = create index emp1 on employee (empid asc) """ mock_s3_client.get_object.return_value = {'Body': mock_body} # 模拟ConfigParser实例的索引访问行为 mock_config_instance = mock_config_parser_cls.return_value mock_section = mock.Mock() mock_section.__getitem__.return_value = 'create index emp1 on employee (empid asc)' mock_config_instance.__getitem__.return_value = mock_section # 调用函数:传入被patch的ConfigParser类,匹配原函数参数要求 result = read_config(mock_config_parser_cls, 'bucket', 'file.ini', 'idx_list', 'idx1') # 断言验证 self.assertEqual(result, ['create index emp1 on employee (empid asc)']) mock_s3_client.get_object.assert_called_with(Bucket='bucket', Key='file.ini') mock_config_instance.__getitem__.assert_called_with('idx_list') mock_section.__getitem__.assert_called_with('idx1') if __name__ == '__main__': unittest.main()
关键修改点
- 修正参数传递:调用
read_config时,第一个参数传入mock_config_parser_cls(被patch的configparser.ConfigParser类),而非之前的Mock实例,匹配原函数对参数的使用逻辑。 - 模拟索引访问:针对原函数的
config[section][key]语法,分别模拟两次__getitem__方法:先让ConfigParser实例返回模拟的section对象,再让section对象返回预期的索引语句。 - 调整断言逻辑:不再验证
get()方法的调用,改为验证两次__getitem__的调用参数是否符合预期。
额外优化建议
原函数的configparser参数设计冗余,可直接在函数内导入模块,简化代码与测试逻辑:
# 优化后的原函数 import configparser import boto3 def read_config(bucket, file, section, key): s3client = boto3.client('s3', region_name='us-east-1') obj = s3client.get_object(Bucket=bucket, Key=file) config = configparser.ConfigParser() config.read_string(obj['Body'].read().decode('utf-8')) return config[section][key].split('\n')
优化后测试无需传递configparser参数,只需patchconfigparser.ConfigParser类即可进一步简化测试。
内容的提问来源于stack exchange,提问作者SreeVik
相关产品推荐
相关产品推荐

