14

私は、大規模なコード ベースのリファクタリングを開始するプロジェクトに参加しています。すぐに発生した問題の 1 つは、各ファイルが他の多くのファイルをインポートすることです。単体テストの作成を開始できるように、実際のコードを変更することなく、単体テストでこれをエレガントな方法でモックするにはどうすればよいですか?

例として: テストしたい関数を含むファイルは、Python コア ライブラリではなく、ソフトウェアの一部である他の 10 個のファイルをインポートします。

単体テストをできるだけ個別に実行できるようにしたいので、今のところ、インポートされているファイルの内容に依存しない関数のみをテストします。

すべての答えをありがとう。

最初は自分が何をしたいのかよくわかっていなかったのですが、今ではわかっていると思います。

問題は、サードパーティの自動魔法のために、アプリケーション全体が実行されている場合にのみ一部のインポートが可能だったことです。そのため、これらのモジュールのスタブを、sys.path で指定したディレクトリに作成する必要がありました。

これで、テストを書きたい関数を含むファイルを単体テスト ファイルにインポートできるようになりました。モジュールが見つからないという不満はありません。

4

5 に答える 5

8

何もインポートしないことを保証しながらモジュールをインポートしたい場合は、__import__組み込み関数を置き換えることができます。

たとえば、次のクラスを使用します。

class ImportWrapper(object):
    def __init__(self, real_import):
        self.real_import = real_import

    def wrapper(self, wantedModules):
        def inner(moduleName, *args, **kwargs):
            if moduleName in wantedModules:
                print "IMPORTING MODULE", moduleName
                self.real_import(*args, **kwargs)
            else:
                print "NOT IMPORTING MODULE", moduleName
        return inner

    def mock_import(self, moduleName, wantedModules):
        __builtins__.__import__ = self.wrapper(wantedModules)
        try:
            __import__(moduleName, globals(), locals(), [], -1)
        finally:
            __builtins__.__import__ = self.real_import

そして、テスト コードでは、 を記述する代わりに、次のimport myModuleように記述します。

wrapper = ImportWrapper(__import__)
wrapper.mock_import('myModule', [])

の 2 番目の引数は、内部モジュールにインポートするモジュールmock_import名のリストです。

この例をさらに変更して、たとえば、インポートしないだけでなく、他のモジュールをインポートしたり、モジュール オブジェクトを独自のカスタム オブジェクトでモックしたりすることもできます。

于 2008-10-07T17:30:27.597 に答える
1

「他の多くのファイルをインポートします」?カスタマイズしたコード ベースの一部である他の多くのファイルをインポートしますか? または、Python ディストリビューションの一部である他の多くのファイルをインポートしますか? それとも、他の多くのオープン ソース プロジェクト ファイルをインポートしますか?

インポートが機能しない場合は、「単純な」PYTHONPATH 問題が発生しています。さまざまなプロジェクト ディレクトリをすべて、PYTHONPATHテストに使用できる に取得します。かなり複雑なパスがありますが、Windows では次のように管理します

@set Part1=c:\blah\blah\blah
@set Part2=c:\some\other\path
@set that=g:\shared\stuff
set PYTHONPATH=%part1%;%part2%;%that%

(a) 物事がどこから来るのかを知り、(b) 物事を移動するときに変更を管理できるように、パスの各部分を別々に保ちます。

PYTHONPATH順番に検索されるため、パスの順序を調整することで、何を使用するかを制御できます。

「すべて」を手に入れると、それは信頼の問題になります。

また

  • 何か (つまり、Python コード ベース) を信頼して、それをインポートするだけです。

または

  • あなたは何か (つまり、あなた自身のコード) を信頼していません。

    1. 個別にテストし、
    2. スタンドアロン テスト用にモックします。

Python ライブラリをテストしますか? もしそうなら、あなたはたくさんの仕事を持っています。そうでない場合は、実際にテストしようとしているものだけをモック化する必要があります。

于 2008-10-07T13:59:18.833 に答える
1

本当に Python のインポート メカニズムをいじりたい場合は、ihooksモジュールを見てください。__import__組み込みの動作を変更するためのツールを提供します。しかし、なぜこれを行う必要があるのか​​ 、あなたの質問からは明らかではありません。

于 2008-10-07T14:05:11.660 に答える
0

上記のコメントでは、特定のモジュールがすでにインポートされていることをPythonに納得させたいと言っています。これはまだ奇妙な目標のように思えますが、それが本当にやりたいことである場合は、原則として、インポートメカニズムの背後に忍び寄り、変更することができますsys.modules。これがパッケージのインポートでどのように機能するかはわかりませんが、絶対的なインポートでは問題ないはずです。

于 2008-10-07T15:30:34.720 に答える
0

単体テストの前に簡単な修正が必要な場合は、難しい操作は必要ありません。

単体テストがテストしたいコードと同じファイルにある場合は、globals()辞書から不要なモジュールを削除するだけです。

かなり長い例を次に示します:impp.pyコンテンツを持つモジュールがあるとします:

value = 5

これで、テスト ファイルに次のように記述できます。

>>> import impp
>>> print globals().keys()
>>> def printVal():
>>>     print impp.value
['printVal', '__builtins__', '__file__', 'impp', '__name__', '__doc__']

imppインポートされたため、グローバルの中にあることに注意してください。printValモジュールを使用する関数の呼び出しはimpp引き続き機能します。

>>> printVal()
5

しかし今、あなたがimppからキーを削除するとglobals()...

>>> del globals()['impp']
>>> print globals().keys()
['printVal', '__builtins__', '__file__', '__name__', '__doc__']

...そして を呼び出してみるとprintVal()、次のようになります。

>>> printVal()
Traceback (most recent call last):
  File "test_imp.py", line 13, in <module>
    printVal()
  File "test_imp.py", line 5, in printVal
    print impp.value
NameError: global name 'impp' is not defined

...おそらくまさにあなたが達成しようとしているものです。

単体テストで使用するには、テスト スイートを実行する直前に、次のようにグローバルを削除できます__main__

if __name__ == '__main__':
    del globals()['impp']
    unittest.main()
于 2008-10-07T14:53:38.130 に答える