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

如何用Python unittest.mock模拟类?附项目结构与代码片段

使用unittest.mock模拟RepositoryMongoDB类测试ServiceOne

先明确你的项目结构:

  • services/service_one.py:待测试的服务模块
  • repository/repository_mongodb.py:提供RepositoryMongoDB类的依赖模块
  • test/services/test_service_one.py:测试文件存放位置

首先放出你提供的repository/repository_mongodb.py代码:

# repository/repository_mongodb.py
class RepositoryMongoDB:
    def __init__(self):
        self.library = []
    def save(self, thing):
        self.library.append(thing)
        return True

我先假设service_one.py的常见业务实现(毕竟你没提供完整代码),比如一个依赖RepositoryMongoDB的服务类:

# services/service_one.py
from repository.repository_mongodb import RepositoryMongoDB

class ServiceOne:
    def __init__(self):
        self.repo = RepositoryMongoDB()
    
    def process_and_save(self, data):
        # 示例业务处理逻辑
        processed_data = f"processed_{data}"
        return self.repo.save(processed_data)

接下来分享两种实用的mock测试方式,你可以根据实际场景选择:

方式一:用@patch装饰器模拟导入的类

这种方式适合不想修改原有ServiceOne代码的场景,核心是mock掉ServiceOne中导入的RepositoryMongoDB类:

# test/services/test_service_one.py
import unittest
from unittest.mock import Mock, patch
from services.service_one import ServiceOne

class TestServiceOne(unittest.TestCase):
    @patch('services.service_one.RepositoryMongoDB')
    def test_process_and_save_calls_repo_save(self, mock_repo_class):
        # 创建mock实例,并指定类实例化时返回这个mock
        mock_repo_instance = Mock()
        mock_repo_class.return_value = mock_repo_instance
        # 设置mock的save方法返回值
        mock_repo_instance.save.return_value = True
        
        # 初始化服务并调用方法
        service = ServiceOne()
        result = service.process_and_save("test_data")
        
        # 验证业务逻辑返回值正确
        self.assertEqual(result, True)
        # 验证RepositoryMongoDB被实例化了一次
        mock_repo_class.assert_called_once()
        # 验证save方法被传入了正确的处理后数据
        mock_repo_instance.save.assert_called_once_with("processed_test_data")

⚠️ 重点提醒:patch的路径是ServiceOne中导入RepositoryMongoDB的路径(也就是services.service_one.RepositoryMongoDB),而不是原始的repository.repository_mongodb.RepositoryMongoDB,因为mock是作用于被测试代码的导入点,路径错了mock会失效。

方式二:依赖注入式mock(更推荐)

如果可以修改ServiceOne的代码,让它支持传入repo实例,测试会更简洁灵活,同时也能降低代码耦合度:

# 修改后的services/service_one.py
from repository.repository_mongodb import RepositoryMongoDB

class ServiceOne:
    # 新增可选参数,默认用真实的RepositoryMongoDB,测试时传入mock
    def __init__(self, repo=None):
        self.repo = repo or RepositoryMongoDB()
    
    def process_and_save(self, data):
        processed_data = f"processed_{data}"
        return self.repo.save(processed_data)

对应的测试代码就不需要用patch了,直接把mock实例传给ServiceOne:

# test/services/test_service_one.py
import unittest
from unittest.mock import Mock
from services.service_one import ServiceOne

class TestServiceOne(unittest.TestCase):
    def test_process_and_save_with_injected_mock(self):
        # 创建一个mock的RepositoryMongoDB实例
        mock_repo = Mock()
        mock_repo.save.return_value = True
        
        # 把mock实例注入到ServiceOne中
        service = ServiceOne(repo=mock_repo)
        result = service.process_and_save("test_data")
        
        # 验证返回值正确
        self.assertEqual(result, True)
        # 验证save方法被正确调用
        mock_repo.save.assert_called_once_with("processed_test_data")

几个关键测试要点

  • 用mock的核心是验证交互:比如确认repo的save方法有没有被调用、传入的参数是否符合预期,而不是测试RepositoryMongoDB本身的功能(那是repo模块的测试任务)。
  • 如果用patch,一定要找准mock路径,否则会出现“mock了但没生效”的问题。
  • 依赖注入的设计不仅方便测试,也让代码的扩展性更好,后续更换存储实现时更轻松。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:28:54