2013-04-24 21 views
14

Występuje tajemniczy błąd importowania, gdy używam testów nosa do uruchomienia zestawu testów, którego nie mogę odtworzyć poza nosem. Ponadto błąd importu znika, gdy pomijam podzbiór testów.Błąd importowania podczas wykonywania testów nosa, których nie mogę odtworzyć poza nosem.

Streszczenie: Dostaję błąd importu w nosie, że a) pojawia się tylko wtedy, gdy testy namiar pewien atrybut są wyłączone oraz b) nie może być powielana w sesji interaktywnej python, nawet kiedy zapewnienia, że ​​sys. ścieżka jest taka sama dla obu.

Szczegóły:

Struktura pakietu wygląda tak:

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 

Niektóre z testów w foo_test.py są powolne, więc stworzyliśmy dekorator @slow aby umożliwić mi pomiń je z opcją 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/") 

Gdy chcę uruchomić tylko szybkich testów jednostkowych, używam

nosetests -vv -a'!slow' 

z katalogu głównego projektu. Kiedy chcę je wszystkie uruchomić, usuwam ostatni argument.

Nadchodzi szczegół, który, jak podejrzewam, jest odpowiedzialny za ten bałagan. Testy jednostkowe muszą załadować dane testowe z plików (w przeciwieństwie do najlepszej praktyki). Pliki są umieszczane w katalogu o nazwie "test_data" w każdym pakiecie testowym, a kod testu jednostkowego odnosi się do nich względną ścieżką, zakładając, że test jednostkowy uruchamiany jest z katalogu test /, jak pokazano w powyższym przykładowym kodzie.

Aby uzyskać to do pracy z systemem nos z katalogu głównego projektu I dodaje następujący kod do Init .py w każdym opakowaniu testu:

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) 

O ile mi zrozumieć, nos wykonuje metody pakietów setUp i tearDown przed i po uruchomieniu testów w tym pakiecie, co zapewnia, że ​​test jednostki może znaleźć odpowiedni katalog test_data, a katalog roboczy zostanie zresetowany do pierwotnej wartości po zakończeniu testów.

Tyle o konfiguracji. Problem polega na tym, że pojawia się błąd importowania tylko po uruchomieniu pełnego zestawu testów. Te same moduły importują się dobrze, gdy wykluczam wolne testy. (Dla wyjaśnienia, testy rzucanie błędy importu nie są powolne, więc wykonać w każdym scenariuszu.)

$ 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 

Jeśli uruchomić zestaw testów bez wolnych testów, to nie błąd:

$ nosetests -a'!slow' 

... 

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

w python interaktywnej sesji, mogę zaimportować moduł testowy bez kłopotów:

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

Kiedy ustawić punkt przerwania w nosie/importer.py, wszystko wygląda inaczej:

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

Widzę te same nieprzyjemne wyniki, nawet jeśli skopiowałem sys.ścieżkę z mojej interaktywnej sesji do sesji pdb i powtórz powyższe. Czy ktokolwiek może mi dać wgląd w to, co się dzieje? Zdaję sobie sprawę, że robię kilka niestandardowych rzeczy w tym samym czasie, co może prowadzić do dziwnych interakcji. Byłbym zainteresowany poradą, jak uprościć moją architekturę, ponieważ chciałbym uzyskać wyjaśnienie tego błędu.

+0

Aby potwierdzić, nie masz "__init __. Py" w projekcie, prawda? – alecxe

+0

Zgadza się. Nie __init__.py w projekcie reż. Widziałem te pytania tam, gdzie to było dyskutowane, ale nie do końca rozumiem, dlaczego to ma znaczenie. Wiesz dlaczego? –

+0

Nie całkiem to rozumiem, ale chodzi o to, jak działa [importer nosa] (https://github.com/nose-devs/nose/blob/master/nose/importer.py). Zobacz także: http://stackoverflow.com/questions/16174649/specially-named-directories-using-nosetests/16224909#16224909. – alecxe

Odpowiedz

8

Oto jak wyśledzić kontekst błędu. Następnie należy sprawdzić plik nose_debug. Wyszukaj komunikat o błędzie "No module named foo_test". Następnie spójrz na poprzednie kilka linii, aby zobaczyć, które pliki/katalogi patrzyły nosem.

W moim przypadku nos próbował uruchomić kod, który zaimportowałem do mojej bazy kodów - moduł trzeciej strony, który zawierał własne testy, ale których nie zamierzałem uwzględnić w moim pakiecie testowym. Aby rozwiązać ten problem, użyłem wtyczki nose-exclude, aby wykluczyć ten katalog.

7

Domyślnie dostosowuje ścieżkę. Zmienimy sys.path przed zaimportowaniem twojego modułu, prawdopodobnie umożliwiając wykonanie podwójnego kodu i importowanie poza pakietem (jak twoja sprawa).

Aby tego uniknąć, skonfiguruj swój telefon PYTHONPATH przed uruchomieniem nosa i użyj nose --no-path-adjustment. Zobacz: http://nose.readthedocs.org/en/latest/usage.html#cmdoption--no-path-adjustment

Jeśli nie można dodać argument wiersza poleceń można użyć var ​​env (NOSE_NOPATH=y) czy to w .noserc:

[nosetests] 
no-path-adjustment=1 
0

I napotkał ten problem i śledzić go na 1) zapominając, aby aktywować virtualenv używałem, i 2) moja skorupa, zsh, najwyraźniej po buforowane ścieżkę do niewłaściwej instancji nosetests wykonywalnego na moim komputerze.

Raz aktywowałem mój virtualenv, a następnie wydałem polecenie powłoki hash -r, błąd ten przestał występować. Przepraszam, nie sprecyzowałem, czy tylko jeden z nich byłby wystarczający.

znalazłem tę odpowiedź za raffienficiaud do wystawiania nosa "nosetest does not honour virtual environments" pomocne:

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)

To Unix.SE odpowiedź na pytanie, "How do I clear Bash's cache of paths to executables?" przez Tobu i 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 .

używam zsh nie bash i hash -d nosetests dał mi komunikat o błędzie. Mimo to problem zniknął po tym, jak zrobiłem hash -r.