34

我已经从一个模块中导入了一个类,但是当我尝试修补类名而不使用它的模块作为前缀时,我得到一个类型错误:

TypeError: Need a valid target to patch. You supplied: 'MyClass'

例如,下面的代码给了我上面的错误:

import unittest
from mock import Mock, MagicMock, patch
from notification.models import Channel, addChannelWithName, deleteChannelWithName, listAllChannelNames

class TestChannel(unittest.TestCase):
    @patch("Channel")
    def testAddChannelWithNamePutsChannel(self, *args):
        addChannelWithName("channel1")
        Channel.put.assert_called_with()

虽然代码的第二个版本没有给我类型错误:

import unittest
from mock import Mock, MagicMock, patch
from notification.models import Channel, addChannelWithName, deleteChannelWithName, listAllChannelNames

class TestChannel(unittest.TestCase):
    @patch("notification.models.Channel")
    def testAddChannelWithNamePutsChannel(self, *args):
        addChannelWithName("channel1")
        Channel.put.assert_called_with()

这是为什么?为什么我可以在其他地方将 Channel 引用为“Channel”,而对于补丁我需要模块前缀才不会出错?另外,我有一种感觉,给出完整的模块前缀也不起作用,因为当我调用 Channel.put.assert_call_with() 时,我得到一个错误,即 assert_call_with 不是 Channel.put 的属性。有人可以解释发生了什么吗?非常感谢!

4

1 回答 1

48

The patch decorator requires the target to be a full dotted path, as stated in the documentation:

target should be a string in the form ‘package.module.ClassName’. The target is imported and the specified object replaced with the new object, so the target must be importable from the environment you are calling patch from. The target is imported when the decorated function is executed, not at decoration time.

"Channel" is just a string, and patch does not have enough information to find the proper class. This is not the same as the name Channel you use elsewhere, which is imported at the top of the module.

第二个测试失败,因为 Channel 被导入到测试模块中,然后patch 用模拟对象替换了 notification.models 中的 Channel。patch 实际所做的是更改在 notification.models 中使用的名称Channel 指向的对象。测试模块中的名称 Channel 已经定义好了,所以不受影响。这实际上在这里得到了更好的解释:https ://docs.python.org/3/library/unittest.mock.html#where-to-patch

要访问对象的修补版本,您可以直接访问模块:

import unittest 
from unittest.mock import patch 
from notification.models import Channel, addChannelWithName  
from notification import models 
                 
class TestChannel1(unittest.TestCase): 
    @patch("notification.models.Channel") 
    def testAddChannelWithNamePutsChannel(self, *args): 
        addChannelWithName("channel1") 
        models.Channel.put.assert_called_with("channel1") 

或者使用作为额外参数传递给装饰函数的修补版本:

class TestChannel2(unittest.TestCase): 
    @patch("notification.models.Channel") 
    def testAddChannelWithNamePutsChannel(self, mock_channel): 
        addChannelWithName("channel1") 
        mock_channel.put.assert_called_with("channel1") 

如果您只想快速修补对象上的单个方法,通常使用patch.object装饰器更容易:

class TestChannel3(unittest.TestCase):
    @patch.object(Channel, 'put')    
    def testAddChannelWithNamePutsChannel(self, *arg): 
        addChannelWithName("channel1") 
        Channel.put.assert_called_with("channel1") 
于 2013-04-17T21:34:38.720 回答