モジュールからクラスをインポートしましたが、モジュールをプレフィックスとして使用せずにクラス名にパッチを適用しようとすると、タイプエラーが発生します。
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()
この2番目のバージョンのコードではタイプエラーは発生しませんが、次のようになります。
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.put.assert_called_with()を呼び出すと、assert_called_withがChannel.putの属性ではないというエラーが発生するため、完全なモジュールプレフィックスを指定しても機能しないように感じます。誰かが何が起こっているのか説明できますか?どうもありがとうございました!
documentation :に記載されているように、patch
デコレータでは、ターゲットが完全な点線のパスである必要があります。
ターゲットは「package.module.ClassName」の形式の文字列である必要があります。ターゲットがインポートされ、指定されたオブジェクトが新しいオブジェクトに置き換えられるため、ターゲットは、パッチを呼び出している環境からインポート可能である必要があります。ターゲットは、装飾時ではなく、装飾された関数の実行時にインポートされます。
"Channel"
は単なる文字列であり、patch
には適切なクラスを見つけるのに十分な情報がありません。これは、モジュールの上部にインポートされている、他の場所で使用しているChannel
という名前と同じではありません。
Channelがテストモジュールにインポートされ、パッチがnotification.modelsのChannelをモックオブジェクトに置き換えるため、2番目のテストは失敗します。パッチが実際に行うことは、notification.models内で使用されるnameチャネルが指すオブジェクトを変更することです。テストモジュールの名前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")
または、decorated関数に追加の引数として渡されたパッチバージョンを使用します。
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")