我从一个模块导入了一个类,但是当我尝试修补类名而没有它的模块作为前缀时,我得到一个类型错误:
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_called_with()时,我得到的错误是assert_called_with不是Channel.put的属性。有人可以解释发生了什么吗?非常感谢!
答案 0 :(得分:30)
patch
装饰器要求目标是完整的虚线路径,如documentation中所述:
target应该是'package.module.ClassName'形式的字符串。导入目标并将指定的对象替换为新对象,因此目标必须可从您调用patch的环境中导入。执行修饰函数时导入目标,而不是在装饰时导入。
"Channel"
只是一个字符串,patch
没有足够的信息来查找正确的类。这与您在别处使用的名称Channel
不同,后者是在模块顶部导入的。
第二个测试失败,因为在测试模块中导入了Channel 然后补丁用notify对象替换notification.models中的Channel。实际上做的补丁是改变对象名称在notification.models指向的频道中使用的频道。测试模块中的名称Channel已经定义,因此不受影响。这实际上在这里有更好的解释:http://www.voidspace.org.uk/python/mock/patch.html#id1
要访问对象的修补版本,您可以直接访问模块:
import unittest
from 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")