简体   繁体   English

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

[英]Mocking a class method and changing some object attributes in Python

I am new to mock in Python. 我是Python的新手。 I want to know how to replace (mock) a class method while testing with another one, knowing that the original just changes some attributes of self without returning any value. 我想知道如何在用另一个方法测试时替换(模拟)类方法,知道原始方法只是改变了self的一些属性而没有返回任何值。 For example: 例如:

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

So here I can't just change the return_value of the mock. 所以在这里我不能只改变模拟的return_value。 I tried to define a new function (that should replace the original) and give it as side_effect to the mock. 我试图定义一个新函数(应该替换原始函数)并将其作为side_effect给mock。 But how can I make the mocking function change attributes of the object in the class. 但是如何使模拟函数更改类中对象的属性。 Here is my code: 这是我的代码:

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

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

some_method.side_effect = replacer

So how does Python now understands the self argument of replacer? 那么Python现在如何理解替换者的self论证? Is that the self of the test class, or the self as the object of the class under test? 这是测试类的自我,还是自我作为被测试类的对象?

Apologies in advance if I don't understand what you are trying to do, but I think this might work: 如果我不明白你想要做什么,请提前道歉,但我认为这可行:

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

This replaces some_method() with other_method() when patched, which sets different values for attributes x, y, and when the test is run, it gives the results: 这在修补时用some_method()替换some_method(),它为属性x,y设置不同的值,并且在运行测试时,它给出结果:

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

OK

EDIT: to answer question about how to do inside the test function without mocking a class... 编辑:回答有关如何在测试函数内部进行操作而不模拟类的问题...

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

Make sure you call start() and stop() on the patcher otherwise you can get into a situation where the patch is active and you don't want it to be. 确保在修补程序上调用start()和stop(),否则可能会遇到修补程序处于活动状态并且您不希望它处于活动状态的情况。 Note that to define the mock function inside the test code function, I didn't use patch as a decorator, because the mock function has to be defined before using the 'new' keyword in patch. 注意,为了在测试代码函数中定义mock函数,我没有使用patch作为装饰器,因为必须在使用patch中的'new'关键字之前定义mock函数。 If you want to use patch as a decorator you have to define the mock function someplace before the patch, defining it inside of MyTestClass also works, but it seems you really want to have the mock function defined inside your test function code. 如果你想使用patch作为装饰器,你必须在补丁之前的某个地方定义mock函数,在MyTestClass中定义它也可以工作,但是你似乎真的想在你的测试函数代码中定义mock函数。

EDIT: added summary of 4 ways I see to do this... 编辑:添加了我看到的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()

None of these uses a side_effect, but they all solve the problem of mocking a class method and changing some attributes. 这些都不使用side_effect,但它们都解决了模拟类方法和更改某些属性的问题。 Which one you choose depends on the application. 您选择哪一个取决于应用程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM