15

ノーズテストを使用してノーズ以外では再現できないテスト スイートを実行すると、不可解なインポート エラーが発生します。さらに、テストのサブセットをスキップすると、インポート エラーが消えます。

エグゼクティブ サマリー: Nose でインポート エラーが発生します。a) 特定の属性を持つテストが除外されている場合にのみ表示され、b) 両方の sys.path が同じであることを確認しても、インタラクティブな python セッションで再現できません。 .

詳細:

パッケージ構造は次のようになります。

project/
    module1/__init__.py
    module1/foo.py
    module1/test/__init__.py
    module1/test/foo_test.py
    module1/test/test_data/foo_test_data.txt
    module2/__init__.py
    module2/bar.py
    module2/test/__init__.py
    module2/test/bar_test.py
    module2/test/test_data/bar_test_data.txt

foo_test.py のいくつかのテストは遅いので、nosetests オプションでそれらをスキップできるように @slow デコレーターを作成しました。

def slow(func):
    """Decorator sets slow attribute on a test method, so 
       nosetests can skip it in quick test mode."""
    func.slow = True
    return func

class TestFoo(unittest.TestCase):

    @slow
    def test_slow_test(self):
        load_test_data_from("test_data/")
        slow_test_operations_here


    def test_fast_test(self):
        load_test_data_from("test_data/")

高速単体テストのみを実行したい場合は、

nosetests -vv -a'!slow'

プロジェクトのルート ディレクトリから。それらをすべて実行したい場合は、最後の引数を削除します。

これが、この混乱のせいだと私が疑う詳細です。単体テストでは、ファイルからテスト データを読み込む必要があります (ベスト プラクティスではないことはわかっています)。ファイルは、各テスト パッケージの "test_data" というディレクトリに配置され、単体テスト コードは相対パスでそれらを参照します。上記のコード例に示すように、単体テストは test/ ディレクトリから実行されています。

プロジェクトのルート ディレクトリからの実行中のノーズでこれを機能させるために、次のコードを各テスト パッケージのinit .pyに追加しました。

import os
import sys

orig_wd = os.getcwd()

def setUp():
    """
    test package setup:  change working directory to the root of the test package, so that 
    relative path to test data will work.
    """
    os.chdir(os.path.dirname(os.path.abspath(__file__)))

def tearDown():
    global orig_wd
    os.chdir(orig_wd)

私が理解している限り、nose はそのパッケージでテストを実行する前後に setUp および TeaDown パッケージ メソッドを実行します。これにより、単体テストが適切な test_data ディレクトリを見つけられるようになり、テスト時に作業ディレクトリが元の値にリセットされます。完了です。

セットアップは以上です。問題は、完全なテスト スイートを実行した場合にのみインポート エラーが発生することです。遅いテストを除外すると、同じモジュールが問題なくインポートされます。(明確にするために、インポート エラーをスローするテストは遅くないため、どちらのシナリオでも実行されます。)

$ nosetests
...

ERROR: Failure: ImportError (No module named foo_test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Library/Python/2.7/site-packages/nose/loader.py", line 413, in loadTestsFromName
    addr.filename, addr.module)
  File "/Library/Python/2.7/site-packages/nose/importer.py", line 47, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/Library/Python/2.7/site-packages/nose/importer.py", line 80, in importFromDir
    fh, filename, desc = find_module(part, path)
ImportError: No module named foo_test

遅いテストなしでテスト スイートを実行すると、エラーは発生しません。

$ nosetests -a'!slow'

...

test_fast_test (module1.test.foo_test.TestFoo) ... ok

Python のインタラクティブ セッションでは、問題なくテスト モジュールをインポートできます。

$ python
Python 2.7.1 (r271:86832, Aug  5 2011, 03:30:24) 
[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import module1.test
>>> module1.test.__path__
['/Users/USER/project/module1/test']
>>> dir(module1.test)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'orig_wd', 'os', 'setUp', 'sys', 'tearDown']

私がnose/importer.pyにブレークポイントを設定すると、様子が異なります:

> /Library/Python/2.7/site-packages/nose/importer.py(83)importFromDir()
-> raise
(Pdb) l
 78                               part, part_fqname, path)
 79                     try:
 80                         fh, filename, desc = find_module(part, path)
 81                     except ImportError, e:
 82                         import pdb; pdb.set_trace()
 83  ->                     raise
 84                     old = sys.modules.get(part_fqname)
 85                     if old is not None:
 86                         # test modules frequently have name overlap; make sure
 87                         # we get a fresh copy of anything we are trying to load
 88                         # from a new path

(Pdb) part
'foo_test'
(Pdb) path
['/Users/USER/project/module1/test']
(Pdb) import module1.test.foo_test
*** ImportError: No module named foo_test
#If I import module1.test, it works, but the __init__.py file is not being executed
(Pdb) import partition.test
(Pdb) del dir
(Pdb) dir(partition.test)
['__doc__', '__file__', '__name__', '__package__', '__path__'] #setUp and tearDown missing?
(Pdb) module1.test.__path__
['/Users/USER/project/module1/test']  #Module path is the same as before.
(Pdb) os.listdir(partition.test.__path__[0])  #All files are right where they should be...
['.svn', '__init__.py', '__init__.pyc', 'foo_test.py', 'foo_test.pyc','test_data']

対話型セッションから pdb セッションに sys.path をコピーして上記を繰り返しても、同じ厄介な結果が表示されます。何が起こっているのかについて誰かが私に洞察を与えることができますか? いくつかの非標準的なことを同時に行っているため、奇妙な相互作用が発生する可能性があることに気付きました。このバグの説明を得るのと同じくらい、アーキテクチャを単純化する方法についてのアドバイスに興味があります。

4

3 に答える 3

7

デフォルトでは、パスを鼻で調整するだけです。モジュールをインポートする前に sys.path を変更し、二重のコード実行とパッケージ外でのインポートを許可する可能性があります(ケースのように)。

これを避けるには、PYTHONPATH鼻水が出る前に をセットアップして を使用しますnose --no-path-adjustment。参照: http://nose.readthedocs.org/en/latest/usage.html#cmdoption--no-path-adjustment

コマンド ライン引数を追加できない場合は、env var ( NOSE_NOPATH=y) またはこれを で使用でき.nosercます。

[nosetests]
no-path-adjustment=1
于 2014-10-27T09:50:21.937 に答える