简体   繁体   English

奇怪的修补行为

[英]Weird patching behavior

I have one class (Cat) and one method (say_hello) to patch. 我有一个类(Cat)和一个方法(say_hello)来修补。 When I patch the class only, everything works well. 当我只修补课程时,一切都运作良好。 When I patch the method only, it works too. 当我仅修补方法时,它也有效。 When I patch both at the same time, the class is not patched but the method is properly patched. 当我同时修补这两个时,该类没有修补,但该方法已正确修补。

main.py main.py

from hello import say_hello
from cat import Cat

cat = Cat('kitty')

def main():
    print(say_hello())

hello.py hello.py

def say_hello():
    return "No mocked"

test.py test.py

import unittest
from unittest.mock import patch

class TestStringMethods(unittest.TestCase):
    # cat.Cat is not patched correctly if both patch statements are there
    @patch('cat.Cat')
    @patch('main.say_hello')
    def test_kitty(self, say_hello_mock, cat_mock):
        say_hello_mock.return_value = "Mocked"
        from main import main
        main()



if __name__ == '__main__':
    unittest.main()

If you run the previous example, a real Cat is created. 如果您运行上一个示例,则会创建一个真正的Cat。 If you comment the patch of main.say_hello, a mock Cat is created. 如果您对main.say_hello的补丁进行评论,则会创建一个模拟Cat。

I don't know why the patch decorator is not working, but you can use this solution: 我不知道为什么补丁装饰器不工作,但你可以使用这个解决方案:

def test_kitty(self):
    with patch('cat.Cat') as cat_mock:
        with patch('main.say_hello') as hello_mock:
            from main import main
            main()

On my question, the first patch called is main.say_hello. 在我的问题上,第一个调用的补丁是main.say_hello。 I will trigger an import of the main module and the cat will be instantiated as a real Cat instance. 我将触发主模块的导入,cat将被实例化为真正的Cat实例。 Then Cat is patched but too late. 然后猫补了,但为时已晚。

The trick is to invert the order of the patch: 诀窍是颠倒补丁的顺序:

@patch('main.say_hello')
@patch('cat.Cat')
def test_kitty(self, cat_mock, say_hello_mock):
    say_hello_mock.return_value = "Mocked"
    from main import main
    main()

As decorators are called in an outward order, the Cat is patched then say_hello is patched triggering an import of the main module (which instantiates a mocked Cat). 当装饰器以向外的顺序调用时,Cat被修补,然后say_hello被修补,触发主模块的导入(实例化一个模拟的Cat)。

The @patch('cat.Cat') call will at first import the cat module. @patch('cat.Cat')调用将首先导入cat模块。 At this point the cat.cat object is created from the original Cat class and it will reference this original class as its type. 此时, cat.cat对象是从原始Cat类创建的,它将引用此原始类作为其类型。

Now you are replacing the original reference to cat.Cat with a reference to the mock. 现在,您将使用对mock的引用替换对cat.Cat的原始引用 This has no effect on the object anymore. 这对对象不再有影响。

You patch an attribute or method of the class and the instance will be change indirectly, but replacing the reference keeps the original class untouched. 您修补了类的属性或方法,实例将间接更改,但替换引用会保持原始类不变。

The say_hello() is defined in hello.py , so this: say_hello()hello.py定义,所以这个:

@patch('main.say_hello')

should be changed to: 应改为:

@patch('hello.say_hello')

then you could decide which to be mocked first in the decorators. 那么你可以决定在装饰器中先嘲笑哪个。

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

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