在Python中模拟类方法并更改某些对象属性

在Python中模拟类方法并更改某些对象属性,python,unit-testing,testing,mocking,patch,Python,Unit Testing,Testing,Mocking,Patch,我不太会用Python进行模拟。我想知道如何在测试时用另一个类方法替换(mock)一个类方法,知道原来的方法只是更改self的一些属性,而不返回任何值。例如: def some_method(self): self.x = 4 self.y = 6 所以这里我不能仅仅改变mock的返回值。我试图定义一个新函数(应该替换原来的函数),并将其作为模拟的副作用。但是如何使mocking函数更改类中对象的属性呢。 这是我的密码: @patch('path.myClas

我不太会用Python进行模拟。我想知道如何在测试时用另一个类方法替换(mock)一个类方法,知道原来的方法只是更改self的一些属性,而不返回任何值。例如:

def some_method(self):   
    self.x = 4   
    self.y = 6   
所以这里我不能仅仅改变mock的返回值。我试图定义一个新函数(应该替换原来的函数),并将其作为模拟的副作用。但是如何使mocking函数更改类中对象的属性呢。 这是我的密码:

@patch('path.myClass.some_method')
def test_this(self,someMethod):

    def replacer(self):
        self.x = 5
        self.y = 16

some_method.side_effect = replacer

那么Python现在如何理解replacer的
self
参数呢?这是测试类的self,还是作为测试类对象的self?

如果我不明白您想做什么,请提前道歉,但我认为这可能有效:

import unittest
from unittest.mock import patch

class MyClass:

    def __init__(self):
        self.x = 0
        self.y = 0

    def some_method(self):   
        self.x = 4   
        self.y = 6    

class OtherClass:

    def other_method(self):
        self.x = 5
        self.y = 16

class MyTestClass(unittest.TestCase):

    @patch('__main__.MyClass.some_method', new=OtherClass.other_method)
    def test_patched(self):
        a = MyClass()
        a.some_method()
        self.assertEqual(a.x, 5)
        self.assertEqual(a.y, 16)

    def test_not_patched(self):
        a = MyClass()
        a.some_method()
        self.assertEqual(a.x, 4)
        self.assertEqual(a.y, 6)

if __name__ == "__main__":
    unittest.main()
这将在修补时用另一个_方法()替换某些_方法(),从而为属性x、y设置不同的值,并在运行测试时给出结果:

..
----------------------------------------------------------------------
Ran 2 tests in 0.020s

OK
编辑:回答关于如何在不模拟类的情况下在测试函数内部执行的问题

def test_inside_patch(self):
    def othermethod(self):
        self.x = 5
        self.y = 16
    patcher = patch('__main__.MyClass.some_method', new=othermethod)
    patcher.start()
    a = MyClass()
    a.some_method()
    self.assertEqual(a.x, 5)
    self.assertEqual(a.y, 16) 
    patcher.stop()
确保在修补程序上调用start()和stop(),否则可能会出现修补程序处于活动状态而您不希望它处于活动状态的情况。请注意,为了在测试代码函数中定义mock函数,我没有使用patch作为装饰器,因为在patch中使用“new”关键字之前必须定义mock函数。如果您想使用补丁作为装饰器,您必须在补丁之前的某个地方定义mock函数,在MyTestClass中定义它也可以,但似乎您确实希望在测试函数代码中定义mock函数

编辑:添加了我看到的4种方法的摘要

# first way uses a class outside MyTest class
class OtherClass:
    def other_method(self):
        ...

class MyTest(unittest.TestCase):

    @patch('path_to_MyClass.some_method', new=OtherClass.other_method)
    def test_1(self)
        ...

    # 2nd way uses class defined inside test class    
    class MyOtherClass:
        def other_method(self):
            ...
    @patch('path_to_MyClass.some_method', new=MyOtherClass.other_method)    
    def test_2(self):
        ...

    # 3rd way uses function defined inside test class but before patch decorator 
    def another_method(self):
        ...
    @patch('path_to_MyClass.some_method', new=another_method)    
    def test_3(self):
        ...

    # 4th way uses function defined inside test function but without a decorator
    def test_4(self):
        def yet_another_method(self):
            ...
        patcher = patch('path_to_MyClass.some_method', new=yet_another_method)
        patcher.start()
        ...
        patcher.stop()

这些都没有副作用,但它们都解决了模拟类方法和更改某些属性的问题。您选择哪一个取决于应用程序。

谢谢您的回答。这很有帮助。但是有必要创建一个新类,然后在这个新类中创建一个新方法吗?我能不能在测试代码中定义另一个方法,然后将其作为副作用?AhmedAyadi是的,可以在测试函数代码中定义mock函数,但我不确定是否使用副作用。查看我的编辑到我的帖子。如果这个答案解决了你的问题,请接受它,谢谢!非常感谢:)这确实回答了我的问题