Импортировать ошибки при запуске носетов, которые я не могу воспроизвести вне носа - программирование
Подтвердить что ты не робот

Импортировать ошибки при запуске носетов, которые я не могу воспроизвести вне носа

Я сталкиваюсь с таинственной ошибкой импорта при использовании nosetests для запуска набора тестов, который я не могу воспроизвести за пределами носа. Кроме того, ошибка импорта исчезает, когда я пропускаю подмножество тестов.

Резюме: Я получаю ошибку импорта в носе, что а) появляется только тогда, когда тесты с определенным атрибутом исключены и б) не могут быть воспроизведены в интерактивном сеансе 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

Некоторые из тестов в 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" в каждом тестовом пакете, а код unit test относится к ним относительным путем, предполагая unit test выполняется из каталога 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)

Насколько я понимаю, нос выполняет методы пакета setUp и tearDown до и после запуска тестов в этом пакете, что гарантирует, что unit test может найти соответствующий каталог test_data, а рабочий каталог reset - исходное значение, когда тесты завершены.

Так много для настройки. Проблема в том, что я получаю ошибку импорта только, когда я запускаю полный набор тестов. Те же самые модули импортируются очень хорошо, когда я исключаю медленные тесты. (Чтобы уточнить, тесты, бросающие ошибки импорта, не замедляются, поэтому они выполняются в любом сценарии.)

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

Когда я устанавливаю точку останова в носу/импортере .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']

Я вижу те же самые жуткие результаты, даже если я скопирую sys.path из своего интерактивного сеанса в сеанс pdb и повторю выше. Может ли кто-нибудь дать мне представление о том, что может происходить? Я понимаю, что одновременно делаю несколько нестандартных вещей, что может привести к странным взаимодействиям. Я был бы заинтересован в совете о том, как упростить мою архитектуру, поскольку я хотел бы получить объяснение этой ошибки.

4b9b3361

Ответ 1

Вот как отследить контекст ошибки.

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

Затем проверьте файл nose_debug. Найдите сообщение об ошибке "No module named foo_test". Затем посмотрите на несколько предыдущих строк, чтобы посмотреть, какие ноты файлов/каталогов просматриваются.

В моем случае нос пытался запустить код, который я импортировал в свою кодовую базу - сторонний модуль, который содержал свои собственные тесты, но который я не собирался включать в свой тестовый набор. Чтобы решить эту проблему, я использовал плагин nose-exclude, чтобы исключить этот каталог.

Ответ 2

Это только нос, регулирующий ваш путь по умолчанию. Он изменит 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

Ответ 3

Я столкнулся с этой проблемой и проследил ее: 1) забыв активировать virtualenv, который я использовал, и 2) мою оболочку, zsh, по-видимому, кэшировал путь к неправильному экземпляру исполняемого файла nosetests на моей машине.

Как только я активировал свой virtualenv, затем дал команду оболочки hash -r, эта ошибка прекратилась. Извините, я не определил, достаточно ли одного из них.

Я нашел этот ответ raffienficiaud, для носа " nosetest не соблюдает виртуальные среды", полезно:

Для записи это проблема bash, которая кэширует команды. В этом случай, which nosetests точки (детерминистически) вправо исполняемый файл, тогда как bash кэширует установленную систему. Использование hash -r очищает кеш (см. http://unix.stackexchange.com/info/5609/how-do-i-clear-bashs-cache-of-paths-to-executables)

Это ответ Unix.SE на вопрос "Как очистить Bash кэш путей к исполняемым файлам?, Тобу и Зигг.

bash кэширует полный путь к команде. Вы можете проверить, что команда, которую вы пытаетесь выполнить, хэшируется с помощью команды type:

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

Очистить весь кеш:

$ hash -r

Или только одна запись:

$ hash -d svnsync

За дополнительной информацией обратитесь к help hash и man bash.

Я использую zsh not bash, а hash -d nosetests дал мне сообщение об ошибке. Тем не менее, проблема исчезла после того, как я сделал hash -r.