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

Python unittest无法Mock私有方法及IndexError问题排查求助

问题分析:ZipService单元测试出现IndexError的原因及解决提示

我有一个核心类ZipService,用于读取S3上的ZIP文件、解压并获取其中manifest.json的内容,功能运行正常。但在使用Python unittest对其进行Mock测试时出现错误,报错信息为IndexError: list index out of range。以下是类代码、测试代码及错误详情,求问题定位提示。

类代码

class ZipService:
    def __init__(self, path: str):
        self.path = path
    def get_manifest_json_object(self):
        s3 = boto3.resource('s3')
        bucket_name, key = self.__get_bucket_and_key()
        bucket = s3.Bucket(bucket_name)
        zip_object_reference = bucket.Object(key).get()["Body"]
        zip_object_bytes_stream = self.__get_zip_object_bytes_stream(zip_object_reference)
        zipf = zipfile.ZipFile(zip_object_bytes_stream, mode='r')
        return self.__get_manifest_json(zipf)

    def __get_bucket_and_key(self):
        pattern = "https:\/\/(.?[^\.]*)\.(.?[^\/]*)\/(.*)"  # this regex is working but don't know how :D
        result = re.split(pattern, self.path)
        return result[1], result[3]

    def __get_zip_object_bytes_stream(self, zip_object_reference):
        return io.BytesIO(zip_object_reference.read())

    def __get_manifest_json(self, zipf):
        manifest_json_text = [zipf.read(name) for name in zipf.namelist() if "/manifest.json" in name][0].decode("utf-8")
        return json.loads(manifest_json_text)   

测试代码

@patch('boto3.resource')
class TestZipService(TestCase):

    def test_zip_service(self, mock):
        s3 = boto3.resource('s3')
        bucket = s3.Bucket("abc")
        bucket.Object.get.return_value = "some-value"
        zipfile.ZipFile.return_value = "/some-path"
        inst = ZipService("/some-path")
        with mock.patch.object(inst, "_ZipService__get_manifest_json", return_value={"a": "b"}) as some_object:
            expected = {"a": "b"}
            actual = inst.get_manifest_json_object()
            self.assertIsInstance(expected, actual)

错误信息

bucket_name, key = self.__get_bucket_and_key()
  File "/Us.tox/py38/lib/python3.8/site-packages/services/zip_service.py", line 29, in __get_bucket_and_key
    return result[1], result[3]
IndexError: list index out of range

问题定位

  1. 正则匹配完全失败:测试中传入的路径是"/some-path",但__get_bucket_and_key方法里的正则是专门匹配S3的HTTPS格式路径(比如https://bucket.s3.amazonaws.com/key.zip),这个测试路径完全不符合正则规则,导致re.split返回的列表长度远小于预期,访问result[1]和result[3]时直接触发索引越界。
  2. 测试逻辑遗漏关键Mock:你只Mock了__get_manifest_json,但get_manifest_json_object执行时会先调用__get_bucket_and_key,这个私有方法没有被Mock,会真实执行,而传入的测试路径不合法,直接引发错误。

解决提示

  • Mock私有方法跳过正则匹配:在测试中直接Mock__get_bucket_and_key,让它返回合法的bucket和key,避免正则匹配环节出错。示例:
    with mock.patch.object(inst, "_ZipService__get_bucket_and_key", return_value=("test-bucket", "test-key.zip")):
        expected = {"a": "b"}
        actual = inst.get_manifest_json_object()
        self.assertEqual(expected, actual)
    
  • 传入符合规则的测试路径:把测试中ZipService的初始化路径改成符合正则的格式,比如"https://test-bucket.s3.amazonaws.com/test-zip.zip"。
  • 修复正则表达式的语法错误:当前正则里的.?是无效的,应该改成.*?(非贪婪匹配),修正后的正则更可靠:r"https://(.*?)\.(.*?)/(.*)"。
  • 修正断言逻辑错误:测试里用了self.assertIsInstance(expected, actual),这个方法是判断actual是不是expected的实例,你实际需要的是判断两个字典内容相等,应该用self.assertEqual(expected, actual)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 02:06:25