2013-04-24 82 views
14

當使用鼻子測試來運行我無法在鼻子之外重現的測試套件時,我遇到了一個神祕的導入錯誤。此外,當我跳過一部分測試時,導入錯誤消失。運行鼻子測試時導入錯誤,我無法在鼻子外重現

摘要:我收到的鼻子輸入錯誤時,測試承載一定的屬性被排除在外和b)不能在交互式Python會話複製一個)只出現,甚至當我保證SYS。兩者的路徑相同。

詳情:

封裝結構是這樣的:

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測試的速度很慢,所以我創建了一個@slow裝飾讓我用nosetests選擇跳過它們:

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」的目錄中,單元測試代碼通過相對路徑引用它們,假設單元測試正在從測試/目錄運行,如上面的示例代碼所示。

爲了得到這個從項目的根目錄流鼻水工作,我下面的代碼添加到初始化的.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) 

據我瞭解,鼻子在該包中運行測試之前和之後執行setUp和tearDown包方法,這確保了單元測試可以找到適當的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 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'] 

當我設置的鼻子/ importer.py斷點,事情看起來不同:在Python交互式會話,我可以毫不費力導入測試模塊

> /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'] 

即使我複製sys,我也會看到相同的扭曲結果。從我的交互式會話到pdb會話的路徑並重覆上述操作。任何人都可以告訴我有關可能發生的事情嗎?我意識到我同時在做一些非標準的事情,這可能會導致奇怪的交互。我會對如何簡化我的架構的建議感興趣,因爲我會爲此錯誤提供解釋。

+0

只是爲了確認,在項目目錄中沒有'__init __。py',對吧? – alecxe 2013-04-26 04:49:18

+0

沒錯。項目目錄中沒有__init__.py。我在討論過這些問題時看到了這些問題,但並不完全明白爲什麼這很重要。你知道爲什麼嗎? – 2013-04-26 22:05:24

+0

我也沒有完全理解它,但它的全部是關於[nose importer](https://github.com/nose-devs/nose/blob/master/nose/importer.py)的工作原理。另請參閱:http://stackoverflow.com/questions/16174649/specially-named-directories-using-nosetests/16224909#16224909。 – alecxe 2013-04-27 03:17:38

回答

8

這裏是如何跟蹤誤差的範圍內。

nosetests --debug=nose,nose.importer --debug-log=nose_debug <your usual args> 

之後,檢查nose_debug文件。搜索您的錯誤消息「No module named foo_test」。然後看看前面的幾行,看看哪些文件/目錄鼻子在看。

在我的情況下,鼻子試圖運行我已經導入到我的代碼庫中的一些代碼 - 第三方模塊包含自己的測試,但我不打算包含在我的測試套件中。爲了解決這個問題,我使用了nose-exclude插件來排除這個目錄。

7

這只是鼻子默認調整你的路徑。它會在導入你的模塊之前改變sys.path,可能允許雙重執行代碼並在包之外導入(就像你的情況一樣)。

爲避免這種情況,請在跑步前設置您的PYTHONPATH並使用nose --no-path-adjustment。請參閱:http://nose.readthedocs.org/en/latest/usage.html#cmdoption--no-path-adjustment

如果你不能在.noserc加你可以使用的環境變量(NOSE_NOPATH=y)或該命令行參數:

[nosetests] 
no-path-adjustment=1 
0

我遇到了這個問題,並追蹤到1)忘記激活我所用的virtualenv,和2)我的殼,zsh,顯然已經緩存到nosetests可執行我的機器上的錯誤的實例的路徑。

一旦我激活了我的virtualenv,然後給了shell命令hash -r,這個錯誤就停止了。對不起,我沒有確定其中只有一個是否足夠。

我發現raffienficiaud這個答覆,鼻子發出 「nosetest does not honour virtual environments」,樂於助人:

For the record, it is a bash issue that caches commands. In that case, which nosetests points (deterministically) to the right executable, while bash cached the system installed one. Using hash -r clears the cache (see http://unix.stackexchange.com/questions/5609/how-do-i-clear-bashs-cache-of-paths-to-executables)

這Unix.SE答案是一個問題, 「How do I clear Bash's cache of paths to executables?」,由東武和Zigg。

bash does cache the full path to a command. You can verify that the command you are trying to execute is hashed with the type command:

$ type svnsync svnsync is hashed (/usr/local/bin/svnsync)

To clear the entire cache:

$ hash -r

Or just one entry:

$ hash -d svnsync

For additional information, consult help hash and man bash .

我用zshbash,並hash -d nosetests給了我一個錯誤消息。不過,在我做了hash -r之後,問題就消失了。