模拟类方法并更改Python中的某些对象属性 [英] Mocking a class method and changing some object attributes in Python

查看:95
本文介绍了模拟类方法并更改Python中的某些对象属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是第一次使用Python进行模拟.我想知道如何在测试另一个类方法的同时替换另一个类方法,因为它知道原始方法只是更改了self的某些属性而没有返回任何值.例如:

I am new to mock in 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. For example:

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

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

So here I can't just change the return_value of the mock. I tried to define a new function (that should replace the original) and give it as side_effect to the 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

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

So how does Python now understands the self argument of replacer? 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()

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

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:

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

OK

回答关于如何在不模拟类的情况下在测试函数中进行操作的问题...

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

请确保在修补程序上调用start()和stop(),否则可能会进入修补程序处于活动状态并且您不希望它处于活动状态的情况.请注意,要在测试代码函数中定义模拟函数,我没有使用patch作为装饰器,因为必须在使用patch中的'new'关键字之前定义模拟函数.如果要将修补程序用作装饰器,则必须在修补程序之前的某个位置定义模拟函数,在MyTestClass内定义模拟函数也可以,但是似乎您确实希望在测试函数代码中定义模拟函数.

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. 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. 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.

添加了我看到的4种方式的摘要...

added summary of 4 ways I see to do this...

# 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,但是它们都解决了模拟类方法和更改某些属性的问题.您选择哪种取决于应用程序.

None of these uses a side_effect, but they all solve the problem of mocking a class method and changing some attributes. Which one you choose depends on the application.

这篇关于模拟类方法并更改Python中的某些对象属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆