如何在同一测试中同时mock Django QuerySet的查询结果与count返回值
解决方案
问题根源在于你直接将order_by的返回值设为普通Python列表,列表本身不存在Django QuerySet自带的count()、values_list()等方法,因此会触发属性不存在的报错。
你可以通过MagicMock模拟QuerySet的完整行为,同时支持遍历操作和QuerySet专属方法调用,修改后的测试代码如下:
# 测试代码 # 1. 先构造模拟的QuerySet对象 mock_queryset = mocker.MagicMock() # 2. 配置遍历返回的Car实例Mock mock_car_list = [mocker.Mock(brand="奔驰"), mocker.Mock(brand="宝马")] mock_queryset.__iter__.return_value = iter(mock_car_list) # 3. 配置count方法返回值 mock_queryset.count.return_value = 2 # 4. 配置values_list方法返回值,匹配业务代码中flat=True的调用场景 mock_queryset.values_list.return_value = [1, 2] # 5. 将order_by的返回值绑定为构造好的模拟QuerySet mock_objects = mocker.patch("你的业务模块路径.Car.objects") mock_objects.filter.return_value.order_by.return_value = mock_queryset # 执行业务方法测试 method_to_mock([0, 1, 2])
原理说明
- 给模拟的QuerySet设置
__iter__魔法方法返回迭代器后,执行for car in cars遍历操作时,Python会自动调用该方法获取迭代对象,效果和遍历真实QuerySet/普通列表完全一致 - 你可以按需给这个模拟QuerySet添加任意QuerySet支持的方法,包括
filter()、first()、exists()等,适配不同业务逻辑的测试需求 - 如果业务代码中会用到
len(cars)的写法,额外添加mock_queryset.__len__.return_value = 对应数值即可支持
注意事项
Mock Django ORM时需要注意路径问题:如果你的业务代码中是from xxx.models import Car后调用Car.objects,那么patch的路径必须是业务代码所在模块的Car路径,而非直接写Car.objects,否则Mock会不生效。
内容的提问来源于stack exchange,提问作者Matthias64
相关产品推荐
相关产品推荐

