如何使用unittest.mock模拟Python类的组合依赖对象?
用unittest.mock模拟外部依赖做单元测试
兄弟,这事儿我熟——单元测试碰到外部API调用就头疼,慢还容易失败,用unittest.mock完全能解决,我给你两种直接能用的方案,一步步来:
核心思路
本质就是把你的CompositionClass里的_redis和_binance_client这两个外部依赖给“掉包”,用mock对象冒充它们。这样调用write或者buy方法时,不会真的去连Redis或者Binance API,只会记录调用的参数和次数,完全不影响外部服务。
方案一:直接替换实例属性
这种方法简单直接,先创建被测类的实例,再手动把它的依赖属性换成mock对象:
import unittest from unittest.mock import Mock from your_module import CompositionClass # 替换成你的实际模块路径 class TestCompositionClass(unittest.TestCase): def test_do_processing(self): # 1. 创建被测类的实例 processor = CompositionClass() # 2. 把外部依赖换成Mock对象 processor._redis = Mock() processor._binance_client = Mock() # 3. 构造测试用的数据 test_data = {"amount_to_buy": 10, "key": "test_value"} # 4. 调用要测试的业务方法 result = processor.do_processing(test_data) # 5. 验证业务逻辑的返回值(替换成你预期的process_val) self.assertEqual(result, expected_process_val) # 6. 验证外部依赖的方法是否被正确调用了 processor._redis.write.assert_called_once_with(test_data) processor._binance_client.buy.assert_called_once_with(10)
方案二:用@patch装饰器模拟依赖类
这种方法更优雅,不用手动替换属性,直接通过装饰器模拟依赖类的导入,让被测类初始化时自动用mock实例:
import unittest from unittest.mock import patch from your_module import CompositionClass class TestCompositionClass(unittest.TestCase): # 注意装饰器的顺序:从下往上对应参数顺序,先写的是最后一个参数 @patch('your_module.Redis') @patch('your_module.BinanceClient') def test_do_processing(self, mock_binance_cls, mock_redis_cls): # 1. 获取mock实例(因为Redis()和BinanceClient()会返回mock对象) mock_redis = mock_redis_cls.return_value mock_binance = mock_binance_cls.return_value # 2. 创建被测类实例,此时内部的_redis和_binance_client都是mock的 processor = CompositionClass() # 3. 构造测试数据 test_data = {"amount_to_buy": 10, "key": "test_value"} # 4. 调用业务方法 result = processor.do_processing(test_data) # 5. 验证返回值 self.assertEqual(result, expected_process_val) # 6. 验证mock方法的调用情况 mock_redis.write.assert_called_once_with(test_data) mock_binance.buy.assert_called_once_with(10)
关键注意点
- patch路径别写错:要patch的是
CompositionClass所在模块中导入的依赖类,而不是依赖类本身的模块。比如你的CompositionClass在myapp/trader.py里,导入的是from redis import Redis,那patch的路径就是myapp.trader.Redis,别直接写redis.Redis,不然会不生效! - 模拟返回值:如果业务逻辑依赖外部调用的返回结果,比如需要判断
redis.write是否成功,你可以给mock方法设置返回值:mock_redis.write.return_value = True,这样代码里就能拿到预设的值。 - 更多断言方法:除了
assert_called_once_with,还有assert_called_with(验证最后一次调用的参数)、assert_not_called(确保方法没被调用),按需使用。
内容的提问来源于stack exchange,提问作者Vino
相关产品推荐
相关产品推荐

