web-dev-qa-db-ja.com

パッチ-相対パッチターゲット名が機能しないのはなぜですか?

モジュールからクラスをインポートしましたが、モジュールをプレフィックスとして使用せずにクラス名にパッチを適用しようとすると、タイプエラーが発生します。

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の属性ではないというエラーが発生するため、完全なモジュールプレフィックスを指定しても機能しないように感じます。誰かが何が起こっているのか説明できますか?どうもありがとうございました!

20
golmschenk

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") 
32
Nicolas Cortot