补丁 - 为什么相对补丁目标名称不起作用?

时间:2013-04-17 12:59:16

标签: python mocking patch

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

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的属性。有人可以解释发生了什么吗?非常感谢!

1 个答案:

答案 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")