【问题标题】:Mocking class properties while using 'autospec=True' in python在python中使用'autospec = True'时模拟类属性
【发布时间】:2012-11-25 09:14:53
【问题描述】:

我希望模拟一个具有以下要求的类:

  • 该类具有公共读/写属性,在其__init__() 方法中定义
  • 该类具有在创建对象时自动递增的公共属性
  • 我希望使用autospec=True,所以类的API会严格检查调用

一个简化的类示例:

class MyClass():
    id = 0

    def __init__(self, x=0.0, y=1.0):
        self.x = x
        self.y = y
        self.id = MyClass._id
        MyClass.id +=1

    def calc_x_times_y(self):
        return self.x*self.y

    def calc_x_div_y(self, raise_if_y_not_zero=True):
        try:
            return self.x/self.y
        except ZeroDivisionError:
            if raise_if_y_not_zero:
                raise ZeroDivisionError
            else:
                return float('nan')

就属性而言,我需要模拟对象的行为与原始对象一样:

  • 它应该自动增加分配给每个新创建的模拟对象的 ID
  • 它应该允许访问其x,y 属性 但是模拟方法调用应该被模拟拦截,并验证其调用签名

最好的解决方法是什么?

编辑

我已经尝试了几种方法,包括继承Mock 类、使用attach_mock()mock_add_spec(),但总是遇到一些死胡同。

我正在使用标准的mock 库。

【问题讨论】:

    标签: python properties attributes mocking subclassing


    【解决方案1】:

    由于没有答案,我将发布对我有用的方法(不一定是最好的方法,但在这里):

    我创建了一个模拟工厂,它创建一个Mock() 对象,使用here 描述的语法设置其id 属性,然后返回该对象:

     class MyClassMockFactory():
         _id = 0
    
         def get_mock_object(self, *args,**kwargs):
            mock = Mock(MyClass, autospec = True)
            self._attach_mock_property(mock , 'x', kwargs['x'])
            self._attach_mock_property(mock , 'y', kwargs['y'])
            self._attach_mock_property(mock , 'id', MyClassMockFactory._id)
            MyClassMockFactory._id += 1
            return mock
    
         def _attach_mock_property(self, mock_object, name, value):
             p = PropertyMock(return_value=value)
             setattr(type(mock_object), name, p)
    

    现在,我可以为我的测试修补 MyClass() 构造函数:

    class TestMyClass(TestCase):
         mock_factory = MyClassMockFactory()
    
         @patch('MyClass',side_effect=mock_factory.get_mock_object)
         test_my_class(self,*args):
             obj0 = MyClass()
             obj1 = MyClass(1.0,2.2)
             obj0.calc_x_times_y()
             # Assertions
             obj0.calc_x_times_y.assert_called_once_with()
             self.assertEqaul(obj0.id, 0)
             self.assertEqaul(obj1.id, 1)
    

    【讨论】:

      【解决方案2】:

      很抱歉挖掘了一个旧帖子,但是可以让您精确地做您想要实现的事情是修补 calc_x_times_ycalc_x_div_y 并在那里设置 autospec=True,而不是模拟创建全班同学。

      类似:

      @patch('MyClass.calc_x_times_y')
      @patch('MyClass.calc_x_div_y')
      test_foo(patched_div, patched_times):
      my_class = MyClass() #using real class to define attributes
      # ...rest of test
      

      【讨论】:

        猜你喜欢
        • 2014-10-24
        • 1970-01-01
        • 2018-09-21
        • 2016-08-29
        • 2017-03-22
        • 2013-05-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多