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

Ahm*_*adi 9 python testing unit-testing patch mocking

我是Python的新手.我想知道如何在用另一个方法测试时替换(模拟)类方法,知道原始方法只是改变了self的一些属性而没有返回任何值.例如:

def some_method(self):   
    self.x = 4   
    self.y = 6   
Run Code Online (Sandbox Code Playgroud)

所以在这里我不能只改变模拟的return_value.我试图定义一个新函数(应该替换原始函数)并将其作为side_effect给mock.但是如何使模拟函数更改类中对象的属性.这是我的代码:

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

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

some_method.side_effect = replacer
Run Code Online (Sandbox Code Playgroud)

那么Python现在如何理解self替换者的论点呢?这是测试类的自我,还是自我作为被测试类的对象?

Ste*_*uta 15

如果我不明白你想要做什么,请提前道歉,但我认为这可行:

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()
Run Code Online (Sandbox Code Playgroud)

这在修补时用some_method()替换some_method(),它为属性x,y设置不同的值,并且在运行测试时,它给出结果:

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

OK
Run Code Online (Sandbox Code Playgroud)

编辑:回答有关如何在测试函数内部进行操作而不模拟类的问题...

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()
Run Code Online (Sandbox Code Playgroud)

确保在修补程序上调用start()和stop(),否则可能会遇到修补程序处于活动状态并且您不希望它处于活动状态的情况.注意,为了在测试代码函数中定义mock函数,我没有使用patch作为装饰器,因为必须在使用patch中的'new'关键字之前定义mock函数.如果你想使用patch作为装饰器,你必须在补丁之前的某个地方定义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()
Run Code Online (Sandbox Code Playgroud)

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