2014-08-27 14 views
8

Das tmpdir Fixture in py.test verwendet den Bereich function und ist daher nicht in einem Fixture mit einem breiteren Bereich wie session verfügbar. Dies wäre jedoch in einigen Fällen nützlich, wie zum Beispiel beim Einrichten eines temporären PostgreSQL-Servers (der natürlich nicht für jeden Test neu erstellt werden sollte).py.test: Temporärer Ordner für den Sitzungsumfang

Gibt es eine saubere Möglichkeit, einen temporären Ordner für einen breiteren Bereich zu bekommen, der nicht das Schreiben eigener Appliances und den Zugriff auf interne APIs von py.test umfasst?

Antwort

13

Leider gibt es derzeit keine Möglichkeit, dies schön zu machen. In Zukunft wird py.test einen neuen "Any" Scope oder etwas ähnliches einführen, aber das ist die Zukunft.

Jetzt müssen Sie dies manuell selbst tun. Wie Sie jedoch bemerken, verlieren Sie einige nette Features: Symlinks in/tmp zum letzten Test, automatische Bereinigung nach ein paar Testläufen, sinnvoll benannte Verzeichnisse usw. Wenn das Verzeichnis nicht zu teuer ist, kombiniere ich normalerweise ein Session- und Funktionsbereichs-Fixture auf folgende Weise:

@pytest.fixture(scope='session') 
def sessiondir(request): 
    dir = py.path.local(tempfile.mkdtemp()) 
    request.addfinalizer(lambda: dir.remove(rec=1)) 
    # Any extra setup here 
    return dir 

@pytest.fixture 
def dir(sessiondir, tmpdir): 
    sessiondir.copy(tmpdir) 
    return tmpdir 

Dadurch entsteht ein temporäres Verzeichnis, das nach einem Testlauf gereinigt wird jedoch für jeden Test, die es tatsächlich braucht (von dir anfordernden) erhält eine Kopie, die mit der tmpdir Semantik gespeichert wird.

Wenn Tests tatsächlich den Status über dieses Verzeichnis freigeben müssen, müsste der Finalizer von dir die Daten zurück in den Sessiondir kopieren. Dies ist jedoch keine sehr gute Idee, da es die Tests abhängig von der Ausführungsreihenfolge macht und auch Probleme mit pytest-xdist verursachen würde.

+0

Würden wir in Schwierigkeiten laufen, wenn wir die eingebaute in tmpdir zu einem neuen Plugin-Namen einfach kopiert und Umfang es, wie wir wollen? – ajwood

+0

Wahrscheinlich, IIRC der Name der Tmpdir hängt von der Funktionsumfang und das könnte zu Problemen führen. Aber ich habe es nicht überprüft, versuch es, wenn du willst! – flub

+0

Seit 2.8 ist dies nicht mehr der Fall. Siehe https://stackoverflow.com/a/38050261/384617. –

-1

Hier ist ein anderer Ansatz. Es sieht so aus, als ob pytest temporäre Verzeichnisse nach Testläufen nicht entfernt. Das Folgende ist ein regulärer Funktionsbereich.

# conftest.py 
TMPDIRS = list() 

@pytest.fixture 
def tmpdir_session(tmpdir): 
    """A tmpdir fixture for the session scope. Persists throughout the session.""" 
    if not TMPDIRS: 
     TMPDIRS.append(tmpdir) 
    return TMPDIRS[0] 

Und über Module statt der gesamten pytest Sitzung persistente temporäre Verzeichnisse haben:

# conftest.py 
TMPDIRS = dict() 

@pytest.fixture 
def tmpdir_module(request, tmpdir): 
    """A tmpdir fixture for the module scope. Persists throughout the module.""" 
    return TMPDIRS.setdefault(request.module.__name__, tmpdir) 

Edit: Hier ist eine andere Lösung, die keine globalen Variablen beinhalten wird. pytest 1.8.0 eingeführt, um eine tmpdir_factory Befestigung, die wir verwenden können:

@pytest.fixture(scope='module') 
def tmpdir_module(request, tmpdir_factory): 
    """A tmpdir fixture for the module scope. Persists throughout the module.""" 
    return tmpdir_factory.mktemp(request.module.__name__) 


@pytest.fixture(scope='session') 
def tmpdir_session(request, tmpdir_factory): 
    """A tmpdir fixture for the session scope. Persists throughout the pytest session.""" 
    return tmpdir_factory.mktemp(request.session.name) 
+0

Warum benutzen Sie nicht 'scope = 'session'' und' scope =' modul' anstatt sich mit globalen Variablen herumzuärgern? – ThiefMaster

+0

Über den nicht entfernenden Teil ist es halbwegs wahr: Es hält die letzten 3 Temp-Dirs herum, was irgendwo in den Dokumenten erwähnt wird, d.h. es ist ein Feature. – ThiefMaster

+0

Sie können scope = 'session' oder 'module' nicht verwenden, wenn Sie das integrierte tmpdir-Gerät verwenden. Sie erhalten eine Exception, die Ihnen mitteilt, dass Sie versuchen, ein funktionsorientiertes Fixture innerhalb eines Session-/Modul-Scope-Fixtures zu verwenden. Daher diese Problemumgehung. – Robpol86

9

Seit pytest Version 2.8 und oberhalb der Session-scoped tmpdir_factory Halterung zur Verfügung. Siehe das folgende Beispiel von documentation.

# contents of conftest.py 
import pytest 

@pytest.fixture(scope='session') 
def image_file(tmpdir_factory): 
    img = compute_expensive_image() 
    fn = tmpdir_factory.mktemp('data').join('img.png') 
    img.save(str(fn)) 
    return fn 

# contents of test_image.py 
def test_histogram(image_file): 
    img = load_image(image_file) 
    # compute and test histogram 
0

Ich füge ein Finalizerthread wenn ich wollen in der Sitzung erstellt alle temporären Ordner löschen.

_tmp_factory = None 
@pytest.fixture(scope="session") 
def tmp_factory(request, tmpdir_factory): 
    global _tmp_factory 
    if _tmp_factory is None: 
     _tmp_factory = tmpdir_factory 
     request.addfinalizer(cleanup) 
    return _tmp_factory 

def cleanup(): 
    root = _tmp_factory.getbasetemp().strpath 
    print "Cleaning all temporary folders from %s" % root 
    shutil.rmtree(root) 

def test_deleting_temp(tmp_factory): 
    root_a = tmp_factory.mktemp('A') 
    root_a.join('foo.txt').write('hello world A') 

    root_b = tmp_factory.mktemp('B') 
    root_b.join('bar.txt').write('hello world B') 

    for root, _, files in os.walk(tmp_factory.getbasetemp().strpath): 
     for name in files: 
      print(os.path.join(root, name)) 

Die Ausgabe sollte wie:

/tmp/pytest-of-agp/pytest-0/.lock 
/tmp/pytest-of-agp/pytest-0/A0/foo.txt 
/tmp/pytest-of-agp/pytest-0/B0/bar.txt 
Cleaning all temporary folders from /tmp/pytest-of-agp/pytest-0