模拟Python类的子集';s方法与性质

模拟Python类的子集';s方法与性质,python,python-unittest,python-mock,python-unittest.mock,Python,Python Unittest,Python Mock,Python Unittest.mock,我正在使用mockPython模块来执行测试 有时我会模拟一个类,但是我只想模拟它的一些方法和属性,而不是全部 假设以下场景: # module.py class SomeClass: def some_method(self): return 100 def another_method(self): return 500 # test.py class Tests(unittest.TestCase): @patch('module

我正在使用
mock
Python模块来执行测试

有时我会模拟一个类,但是我只想模拟它的一些方法和属性,而不是全部

假设以下场景:

# module.py
class SomeClass:
    def some_method(self):
        return 100

    def another_method(self):
        return 500

# test.py
class Tests(unittest.TestCase):
    @patch('module.SomeClass')
    def test_some_operation(self, some_class_mock):
        some_class_instance = some_class_mock.return_value

        # I'm mocking only the some_method method.
        some_class_instance.some_method.return_value = 25

        # This is ok, the specific method I mocked returns the value I wished.
        self.assertEquals(
            25,
            SomeClass().some_method()
        )

        # However, another_method, which I didn't mock, returns a MagicMock instance
        # instead of the original value 500
        self.assertEquals(
            500,
            SomeClass().another_method()
        )
在上面的代码中,一旦我修补了
SomeClass
类,就会调用返回\u值的方法 我没有正确设置将返回
MagicMock
对象

我的问题是:如何只模拟一些类方法,而保持其他方法不变

我可以想出两种方法,但没有一种是真正好的

  • 一种方法是将mock的方法设置为原始类方法,如下所示:

    some_class_instance.another_method = SomeClass.another_method
    
    这并不是真正需要的,因为类可能有很多方法和属性要处理 “卸下”

  • 另一种方法是显式修补我想要的每个方法,例如:

     @patch('module.SomeClass.some_method')
     def test_some_operation(self, some_method_mock):
    
    但是如果我想模拟类本身,模拟对 例如初始值设定项。下面的代码将覆盖所有
    SomeClass
    的方法

     @patch('module.SomeClass.some_method')
     @patch('module.SomeClass')
     def test_some_operation(self, some_class_mock, some_method_mock):
    
  • 下面是一个更具体的例子:

    class Order:
        def process_event(self, event, data):
            if event == 'event_a':
                return self.process_event_a(data)
    
            elif event == 'event_b':
                return self.process_event_b(data)
    
            else:
                return None
    
        def process_event_a(self, data):
            # do something with data
    
        def process_event_b(self, data):
            # do something different with data
    
    在本例中,我有一个通用方法
    process\u event
    ,它根据提供的事件调用特定的处理事件

    我只想测试方法
    process\u event
    。我只是想知道是否根据我提供的事件调用了适当的特定事件


    因此,在我的测试用例中,我想做的是模拟
    process\u event\u a
    process\u event\u b
    ,使用特定参数调用原始
    process\u event
    ,然后使用正确的参数来断言调用了
    process\u event\u a
    process\u event\u b

    必须修补对象,而不是修补整个类。也就是说,创建类的实例,然后修补该实例的方法

    请注意,您也可以使用decorator
    @patch.object
    而不是我的方法

    class SomeClass:
        def some_method(self):
            return 100
    
        def another_method(self):
            return 500
    
    在您的
    test.py中

    from unittest import mock
    
    class Tests(unittest.TestCase):
        
    
        def test_some_operation(self):
            some_class_instance = SomeClass()
    
            # I'm mocking only the some_method method.
            with mock.patch.object(some_class_instance, 'some_method', return_value=25) as cm:
    
                # This is gonna be ok
                self.assertEquals(
                    25,
                    SomeClass().some_method()
                )
    
                # The other methods work as they were supposed to.
                self.assertEquals(
                    500,
                    SomeClass().another_method()
                )
    

    你不应该模仿你应该测试的部分。如果你发现你想部分模仿某些东西,这是一个建议,你需要重新考虑这个方法。你应该只在界面上使用模仿。如果您正在测试的对象需要一个对象来处理,并且您希望其中一些对象是真实的,而另一些对象是错误的,那么您的接口可能太大了。简化攻击面,以便明确应该在何处实施模拟。如果不清楚,再考虑一下你的设计。嗨,乔恩和保罗,谢谢你们的回答。好吧,我理解你认为我不应该那样做。。。这是否意味着没有比我在问题上发布的更好的方法了?你的例子太抽象了,说不出来。您的测试只调用一个mock,所以不清楚您实际要测试什么。用于测试他们的合作者,在这种情况下,您应该测试使用
    SomeClass
    @jornsharpe的任何东西,再次感谢。我在这个问题上添加了一个更具体的例子,我相信这说明了部分模拟类是有意义的!感谢您指出patch.object的用法。我必须执行一个小的更正,因为在方法实现中使用时,它必须用作上下文管理器。