【问题标题】:How do I mock Python properties at an instance level?如何在实例级别模拟 Python 属性?
【发布时间】:2019-08-29 18:19:17
【问题描述】:

正如unittest.mock documentation 指出的那样:

由于模拟属性的存储方式,您不能直接将 PropertyMock 附加到模拟对象。相反,您可以将其附加到模拟类型对象

但是,我遇到了一些情况,我想在单个测试中模拟多个类实例上的多个属性值。

例如,假设我有一个带有availability 属性的TimeSlot 类:

class TimeSlot:
    @property
    def availability(self):
        # Run a complex DB query to determine availability

...我有一个帮助类,它按availabilityTimeSlot 实例列表进行排序:

from unittest.mock import Mock

from app.helpers import sort_slots
from app.models import TimeSlot


def test_sort_slots_reorders_by_descending_availability():
    slot_a = TimeSlot()
    slot_a.availability = Mock(return_value=0.1) # BOOM!

    slot_b = TimeSlot()
    slot_b.availability = Mock(return_value=0.2)

    slots = [slot_a, slot_b]

    assert sort_slots(slots) == [slot_b, slot_a]

当我尝试将 Mock(或 PropertyMock)分配给 availability 属性时,运行此测试会引发错误:

AttributeError: can't set attribute

有什么方法可以模拟 Python 类的每个实例的属性返回值?

如果没有,我应该以不同的方式来编写这样的单元测试吗?

谢谢!

【问题讨论】:

    标签: python unit-testing python-unittest python-unittest.mock


    【解决方案1】:

    availability 属性是在 TimeSlot 类上定义的,而不是实例,因此您需要用类级别的东西替换它。在此示例中,我将其替换为一个类属性,然后用实例属性覆盖该属性。

    from unittest.mock import patch
    
    class TimeSlot:
        @property
        def availability(self):
            return 0.0
    
    slot1 = TimeSlot()
    slot2 = TimeSlot()
    with patch('__main__.TimeSlot.availability', 0.0):
        slot1.availability = 0.1
        slot2.availability = 0.2
    
        assert slot1.availability == 0.1
        assert slot2.availability == 0.2
    

    with 块结束时,原来的availability 属性将被放回原处。

    【讨论】:

    • 注意:patch('main.TimeSlot.availability', 0.0) 对我不起作用。我不得不选择完整的模块路径而不是 main
    • 在这个例子中,@Skratt, __main__ 是完整的模块路径。不过,你是对的,如果我正在修补 myapp.timeslot.TimeSlot 类,那么我需要修补 myapp.timeslot.TimeSlot.availability
    【解决方案2】:

    您应该模拟 TimeSlot 对象本身。这允许更改 availability 属性的行为方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-27
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      • 2020-06-17
      • 2012-08-30
      相关资源
      最近更新 更多