7

我希望模拟具有以下要求的课程:

  • 该类具有公共读/写属性,在其__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类化、 useattach_mock()和 and mock_add_spec(),但总是遇到一些死胡同。

我正在使用标准的模拟库。

4

2 回答 2

3

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

我创建了一个模拟工厂,它创建一个Mock()对象,使用此处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)
于 2012-11-29T08:40:38.387 回答
0

很抱歉挖掘了一个旧帖子,但是可以让您精确地完成您想要实现的目标是修补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
于 2015-06-18T18:40:14.563 回答