2013-04-17 20 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の属性ではないというエラーが発生するため、モジュール全体のプレフィックスを与えることはできません。誰かが何が起こっているのか説明できますか?大変ありがとう! documentationに述べたように

答えて

16

patchデコレータは、完全な点線のパスにターゲットを必要とします。

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と同じではなく、モジュールの上部にインポートされます。

チャネルがテストモジュールでインポートされるため、2番目のテストは失敗します次にパッチは、notification.modelsのチャネルをmockオブジェクトに置き換えます。どのパッチが実際にオブジェクトを変更するのですか notification.models内で使用されるチャネルがを指しています。テストモジュールのチャネル名は既に定義されているため、影響を受けません。これは実際にはより良いここで説明: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

第3のオプションは、オブジェクトのメソッドだけでなく、モジュールのクラス全体を嘲笑うためにも働きます。これはまさに私が探していたものです。ありがとうございました! – balu

関連する問題