2013-04-17 68 views
12

我已經導入一個類從一個模塊,但是當我嘗試修補的類名沒有它的模塊作爲前綴,我得到一個錯誤類型:補丁 - 爲什麼相對補丁目標名稱不起作用?

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的屬性。有人可以解釋發生了什麼嗎?非常感謝!

回答

16

patch裝飾要求的目標是一個完整的虛線路徑,如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"只是一個字符串,patch沒有足夠的信息來找到正確的類。這與您在別處使用的名稱Channel不一樣,該名稱在模塊的頂部導入。

第二次測試失敗,因爲Channel被導入到測試模塊,然後補丁用一個模擬對象替換了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") 
+0

非常具有描述性,你涵蓋了兩個問題。非常感謝。 – golmschenk

+0

第三個選項不僅適用於對象的方法,還適用於嘲笑模塊的整個類 - 這正是我所需要的。謝謝! – balu