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

gol*_*enk 19 python patch mocking

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

TypeError: Need a valid target to patch. You supplied: 'MyClass'
Run Code Online (Sandbox Code Playgroud)

例如,以下代码给出了上述错误:

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()
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

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

Nic*_*tot 32

patch装饰要求的目标是一个完整的虚线路径,如邮件中注明文档:

target应该是'package.module.ClassName'形式的字符串.导入目标并将指定的对象替换为新对象,因此目标必须可从您调用patch的环境中导入.执行修饰函数时导入目标,而不是在装饰时导入.

"Channel"只是一个字符串,并patch没有足够的信息来找到合适的类.这与Channel您在别处使用的名称不同,后者在模块顶部导入.

第二个测试失败,因为Channel在测试模块中导入,然后 patch使用模拟对象替换notification.models中的Channel.实际上做了什么补丁更改了在notification.models指向的Channel中使用的名称对象.测试模块中的名称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") 
Run Code Online (Sandbox Code Playgroud)

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

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

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

class TestChannel3(unittest.TestCase):
    @patch.object(Channel, 'put')    
    def testAddChannelWithNamePutsChannel(self, *arg): 
        addChannelWithName("channel1") 
        Channel.put.assert_called_with("channel1") 
Run Code Online (Sandbox Code Playgroud)

  • 如果您要模拟的对象不在不同的模块中,请使用 `patch("__main__.my_obj")` (3认同)