运行鼻子测试时输入错误,我无法在鼻子外面重现

Ale*_*sha 14 python unit-testing nose importerror

当我使用nosetests来运行一个我无法在鼻子外面重现的测试套件时,我遇到了一个神秘的导入错误.此外,当我跳过测试的子集时,导入错误消失.

执行摘要: 我在Nose中收到导入错误a)仅在排除带有某个属性的测试时出现,并且b)无法在交互式python会话中重现,即使我确保sys.path对两者都相同.

细节:

包结构如下所示:

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
Run Code Online (Sandbox Code Playgroud)

一些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/")
Run Code Online (Sandbox Code Playgroud)

当我想只运行快速单元测试时,我会使用

nosetests -vv -a'!slow'
Run Code Online (Sandbox Code Playgroud)

从项目的根目录.当我想要运行它们时,我删除了最后的参数.

我怀疑是这个烂摊子应该归咎于细节.单元测试需要从文件加载测试数据(不是最佳实践,我知道.)文件放在每个测试包中名为"test_data"的目录中,单元测试代码通过相对路径引用它们,假设正在从test /目录运行unit test,如上面的示例代码所示.

为了从项目的根目录中运行nose,我在每个测试包中将以下代码添加到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)
Run Code Online (Sandbox Code Playgroud)

据我所知,nose在该包中运行测试之前和之后执行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
Run Code Online (Sandbox Code Playgroud)

如果我在没有慢速测试的情况下运行测试套件,那么没有错误:

$ nosetests -a'!slow'

...

test_fast_test (module1.test.foo_test.TestFoo) ... ok
Run Code Online (Sandbox Code Playgroud)

在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']
Run Code Online (Sandbox Code Playgroud)

当我在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']
Run Code Online (Sandbox Code Playgroud)

即使我将sys.path从我的交互式会话复制到pdb会话中并重复上述操作,我也会看到相同的结果.任何人都可以给我任何有关可能发生的事情的见解吗?我意识到我正在同时做几件非标准的事情,这可能会导致奇怪的互动.我对如何简化我的架构的建议感兴趣,因为我会得到这个bug的解释.

jdh*_*deb 9

以下是如何跟踪错误的上下文.

nosetests --debug=nose,nose.importer --debug-log=nose_debug <your usual args>
Run Code Online (Sandbox Code Playgroud)

然后,检查nose_debug文件.搜索您的错误消息" No module named foo_test".然后查看前面几行,看看nose正在查看哪些文件/目录.

在我的情况下,鼻子试图运行一些我导入我的代码库的代码 - 第三方模块包含自己的测试,但我不打算包含在我的测试套件中.为了解决这个问题,我使用了nose-exclude插件来排除这个目录.


ion*_*lmc 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
Run Code Online (Sandbox Code Playgroud)