2013-04-24 14 views
14

Ich stoße auf einen mysteriösen Importfehler bei der Verwendung von Nasetests, um eine Testsuite auszuführen, die ich außerhalb der Nase nicht reproduzieren kann. Darüber hinaus verschwindet der Importfehler, wenn ich eine Teilmenge der Tests überspringe.Importfehler beim Ausführen von Nasetests, die ich außerhalb der Nase nicht reproduzieren kann

Zusammenfassung: Ich bekomme einen Importfehler in Nase, dass a) nur erscheint, wenn Tests mit einem bestimmten Attribut ausgeschlossen sind und b) nicht in einer interaktiven Python-Sitzung reproduziert werden kann, auch wenn ich dafür sorge. Der Weg ist für beide gleich.

Details:

Die Paketstruktur sieht wie folgt aus:

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 

Einige der Tests in foo_test.py langsam sind, so habe ich ein @slow Dekorateur erstellt mir zu erlauben, überspringen sie mit einer nosetests Option:

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/") 

Wenn ich nur die schnelle Unit-Tests ausführen möchten, verwende ich

nosetests -vv -a'!slow' 

aus dem Stammverzeichnis des Projekts. Wenn ich sie alle ausführen will, entferne ich das letzte Argument.

Hier kommt das Detail, von dem ich vermute, dass es für dieses Chaos verantwortlich ist. Die Komponententests müssen Testdaten aus Dateien laden (nicht Best Practice, die ich kenne.) Die Dateien werden in jedem Testpaket in ein Verzeichnis namens "test_data" gestellt, und der Komponententestcode bezieht sich auf sie durch einen relativen Pfad Der Komponententest wird aus dem test/-Verzeichnis ausgeführt, wie im obigen Beispielcode gezeigt.

dies mit laufender Nase aus dem Stammverzeichnis des Projektes zur Arbeit zu kommen, habe ich den folgenden Code init Py in jedem Paket Test:

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) 

Soweit ich verstehe, Nase Führt die Paketmethoden setUp und tearDown vor und nach dem Ausführen der Tests in diesem Paket aus. Dadurch wird sichergestellt, dass der Komponententest das entsprechende Verzeichnis test_data finden kann. Wenn die Tests abgeschlossen sind, wird das Arbeitsverzeichnis auf den ursprünglichen Wert zurückgesetzt.

So viel für die Einrichtung. Das Problem ist, bekomme ich einen Importfehler nur, wenn ich die vollständige Suite von Tests ausführen. Die gleichen Module importieren sich gut, wenn ich die langsamen Tests ausschließe. (Um zu klären, werfen die Tests Importfehler sind nicht langsam, so dass sie durchführen in beiden Szenarien.)

$ 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 

Wenn ich die Testsuite ohne die langsamen Tests laufen, dann kein Fehler:

$ nosetests -a'!slow' 

... 

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

in einer python interaktive Sitzung, kann ich das Testmodul ohne Probleme importieren:

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

Wenn ich einen Haltepunkt in der Nase/importer.py gesetzt, sieht die Sache anders aus:

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

Ich sehe die gleichen screwy Ergebnisse, auch wenn ich sys kopieren.Pfad von meiner interaktiven Sitzung in die PDB-Sitzung und wiederholen Sie die oben genannten. Kann mir irgendjemand etwas darüber sagen, was vor sich geht? Ich merke, dass ich mehrere nicht standardmäßige Dinge gleichzeitig mache, was zu seltsamen Interaktionen führen kann. Ich wäre genauso interessiert an Ratschlägen, wie ich meine Architektur vereinfachen könnte, um eine Erklärung für diesen Fehler zu erhalten.

+0

Nur um zu bestätigen, haben Sie nicht '__init __. Py' im Projektverzeichnis, richtig? – alecxe

+0

Das stimmt. Nein __init__.py im Projektverzeichnis Ich habe diese Fragen gesehen, wo das diskutiert wurde, aber verstehe nicht ganz, warum das wichtig ist. Weißt du, warum? –

+0

Ich verstehe es auch nicht ganz, aber es geht darum, wie [Nose Importeur] (https://github.com/nose-devs/nose/blob/master/nose/importer.py) funktioniert. Siehe auch: http://StackOverflow.com/Questions/16174649/Special-Anamed-Directories-using-Nosetests/16224909#16224909. – alecxe

Antwort

8

Hier ist, wie auf der Spur zu dem Kontext des Fehlers.

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

Danach überprüfen Sie die nose_debug Datei. Suchen Sie nach Ihrer Fehlermeldung "No module named foo_test". Schauen Sie sich dann die vorhergehenden Zeilen an, um zu sehen, welche Dateien/Verzeichnisse die Nase haben.

In meinem Fall versuchte Nase, etwas Code auszuführen, den ich in meine Codebasis importiert hatte - ein 3rd-Party-Modul, das seine eigenen Tests enthielt, die ich aber nicht in meine Testsuite aufnehmen wollte. Um dies zu beheben, habe ich das nose-exclude Plugin verwendet, um dieses Verzeichnis auszuschließen.

7

Es ist nur die Nase, die Ihren Weg standardmäßig einstellt. Es wird sys.path vor dem Importieren Ihres Moduls ändern, was möglicherweise die Ausführung von Doppelcode und das Importieren außerhalb des Pakets (wie in Ihrem Fall) ermöglicht.

Um dies zu vermeiden, richten Sie Ihre PYTHONPATH vor der Nase und verwenden Sie nose --no-path-adjustment. Siehe: http://nose.readthedocs.org/en/latest/usage.html#cmdoption--no-path-adjustment

Wenn Sie kein Befehlszeilenargument hinzufügen, können Sie ein env var (NOSE_NOPATH=y) oder diese in .noserc verwenden können:

[nosetests] 
no-path-adjustment=1 
0

traf ich dieses Problem, und verfolgte sie bis 1) zu vergessen die virtualenv war ich mit, und 2) meine Schale, zsh, offenbar den Weg auf meine Maschine an die falsche Instanz der nosetests ausführbare zwischengespeichert haben zu aktivieren.

Sobald ich meine virtualenv aktiviert, dann gab der Shell-Befehl hash -r, dieser Fehler aufgehört zu erscheinen. Entschuldigung, ich habe nicht festgestellt, ob nur eine davon ausreichend gewesen wäre.

fand ich diese Antwort von raffienficiaud, an Nase Thema "nosetest does not honour virtual environments", hilfreich:

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)

Die Unix.SE Antwort auf eine Frage ist, "How do I clear Bash's cache of paths to executables?" von Tobu und 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 .

Ich benutze zsh nicht bash und hash -d nosetests gab mir eine Fehlermeldung. Trotzdem war das Problem weg nachdem ich hash -r gemacht habe.