web-dev-qa-db-ja.com

Python内のfrom / importステートメントのモックパッチ

次のサンプルコードでmock.patchを機能させようとしています:

from mock import patch
from collections import defaultdict

with patch('collections.defaultdict'):
  d = defaultdict()
  print 'd:', d

これは以下を出力します:

d: defaultdict(None, {})

つまり、defaultdictにはパッチが適用されていません。

From/importステートメントをストレートインポートステートメントに置き換えると、機能します。

from mock import patch
import collections

with patch('collections.defaultdict'):
 d = collections.defaultdict()
 print 'd:', d

出力は次のとおりです。

d: <MagicMock name='defaultdict()' id='139953944084176'>

From/importを使用して呼び出しにパッチを適用する方法はありますか?

ありがとうございました

27
oneself

同じモジュール内の何かにパッチを適用する場合は、__main__を使用できます。

from mock import patch
from collections import defaultdict

with patch('__main__.defaultdict'):
    d = defaultdict()
    print 'd:', d

ただし、インポートしたモジュールのモックを作成している場合は、そのモジュールの名前を使用して、正しい参照(または名前)にパッチを適用する必要があります。

# foo.py

from collections import defaultdict

def bar():
    return defaultdict()


# foo_test.py    

from mock import patch
from foo import bar

with patch('foo.defaultdict'):
    print bar()

ここでのポイントは、patchがパッチを適用する対象へのフルパスを必要としているということです。現在のモジュールで何かにパッチを当てるとき、これは少し奇妙に見えるだけです。なぜなら、人々は__main__を頻繁に使用しないためです(または、現在のモジュールを参照する必要があるためです)。

38
Adam Wagner

patchは、namesにパッチを適用することで機能します。オブジェクトにアクセスするためにdefaultdict(ローカル名前空間内)の名前を使用している場合、名前collections.defaultdictにパッチを適用しても何もできません。 http://www.voidspace.org.uk/python/mock/patch.html#id1 にあるドキュメントを参照してください。

10
BrenBarn

この場合、名前は非常に混乱する可能性があります。私たちは常に名前空間のクラス定義をモックしたいと思っています。名前空間は、インポートが行われるモジュールです。クラス定義は、その名前空間で使用される名前です。

具体的な例を見てみましょう:

  • myproj.utilitiesモジュールにはActorクラスが含まれています
  • myproj.applicationはこれをfrom myproj.utilities import Actorとしてインポートします
  • 私のテストはmy.proj.applicationを実行し、アクターをモックする必要があります

myproj.utilities.py

class Actor:
    def __init__(name):
        self.name = name

myproj.application.py

from myproj.utilities import Actor

class App:
    def __init__(name):
        self.actor = Actor(name)

テストコード

from mock import patch
from myproj.application import App

test:
  # format: patch('<namespace>.<Class>')
  # the namespace in which we with to mock
  # the class definition we wish to mock
  with patch('myproj.application.Actor'):
      app = App('Someone')
      print( type(app.actor) ) # expect a MagicMock

私は他のいくつかのアプローチを試しましたが、これは私にとってうまくいきます。上記のコードはテストしていませんが、自分の特定のケースから一般化しています。したがって、少しずれている可能性があります。

1
Peter Kahn