如何Mock父类方法实现子类Mongo旧数据清理逻辑的单元测试
解决方案
1. 核心逻辑说明
你通过装饰器拿到的connect_mock、date_mock本身就是MagicMock对象,不需要替换对象本身,直接给它们的return_value属性赋值为你想要的自定义返回值即可。另外注意装饰器的参数顺序是从下到上和装饰器顺序对应,你之前写的参数顺序是反的,这也是常见的出错点。
2. 完整测试代码示例
import mongomock from datetime import datetime from unittest import mock # 导入你自己实现的子类 from your_module import YourSubClass @mock.patch("path_to_base_class.BaseClass.connect_to_collection") @mock.patch("path_to_base_class.BaseClass.get_date_from_input") def test_remove_old_docs(date_mock, connect_mock): # 设置从HDFS读取日期的返回值为指定datetime test_date = datetime(2020, 1, 1) date_mock.return_value = test_date # 构造mongomock内存集合,插入测试数据 mocked_collection = mongomock.MongoClient().db.collection mocked_collection.insert_many([ {'_id': '1', 'time': datetime(2010, 10, 10, 10, 10, 10)}, {'_id': '2', 'time': datetime(2015, 10, 10, 10, 10, 10)}, {'_id': '3', 'time': datetime(2020, 10, 10, 10, 10, 10)}, {'_id': '4', 'time': datetime(2030, 10, 10, 10, 10, 10)}, {'_id': '5', 'time': datetime(2040, 10, 10, 10, 10, 10)}, ]) # 设置Mongo连接方法的返回值为这个内存集合 connect_mock.return_value = mocked_collection # 实例化子类,执行业务方法 sub_obj = YourSubClass() sub_obj.remove_old_sources() # 验证执行结果,示例中早于2020-01-01的2条数据应该被删除 assert mocked_collection.count_documents({}) == 3 # 可选:验证方法调用参数是否符合预期 date_mock.assert_called_once() connect_mock.assert_called_once_with("你的数据库名", "你的集合名")
3. 上下文管理器写法(更稳妥,避免路径写错)
如果你不想用装饰器写法,也可以直接patch子类继承的方法,代码如下:
def test_remove_old_docs(): test_date = datetime(2020, 1, 1) mocked_collection = mongomock.MongoClient().db.collection # 插入测试数据的逻辑和上面一致,此处省略 with mock.patch.object(YourSubClass, "get_date_from_input", return_value=test_date): with mock.patch.object(YourSubClass, "connect_to_collection", return_value=mocked_collection): sub_obj = YourSubClass() sub_obj.remove_old_sources() # 断言逻辑和上面一致
常见问题排查
- 如果发现mock不生效,大概率是patch路径写错了,遵循patch实际调用位置的原则:如果你的子类在
a.b.c模块中导入继承父类,就patcha.b.c.BaseClass.方法名,不要用父类原始定义的模块路径。 delete_stale_documents不需要额外mock,它接收mongomock的集合对象后会直接在内存中执行删除逻辑,刚好可以验证你的删除逻辑是否正确。
内容的提问来源于stack exchange,提问作者Zsolt Pentek
相关产品推荐
相关产品推荐

