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

时间:2015-12-22 00:51:19

标签: python unit-testing testing mocking patch

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

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

所以在这里我不能只改变模拟的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

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

1 个答案:

答案 0 :(得分:12)

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

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()

这会在修补时将some_method()替换为other_method(),它为属性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(),否则您可能会遇到修补程序处于活动状态并且您不希望它出现的情况。请注意,为了在测试代码函数中定义模拟函数,我没有使用补丁作为装饰器,因为必须在使用' new'之前定义模拟函数。补丁中的关键字。如果你想使用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()

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