Suche…


Py.test einrichten

py.test ist eine von mehreren Test-Bibliotheken von Drittanbietern , die für Python verfügbar sind. Es kann mit pip installiert werden

pip install pytest

Der zu testende Code

projectroot/module/code.py wir an, wir testen eine Zusatzfunktion in projectroot/module/code.py :

# projectroot/module/code.py
def add(a, b):
    return a + b

Der Prüfcode

Wir erstellen eine Testdatei in projectroot/tests/test_code.py . Die Datei muss mit test_ beginnen , um als test_ erkannt zu werden.

# projectroot/tests/test_code.py
from module import code


def test_add():
    assert code.add(1, 2) == 3

Den Test ausführen

Von projectroot wir einfach py.test :

# ensure we have the modules
$ touch tests/__init__.py
$ touch module/__init__.py
$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items

tests/test_code.py .

================================================ 1 passed in 0.01 seconds ================================================

Fehlgeschlagene Tests

Ein fehlgeschlagener Test liefert hilfreiche Informationen darüber, was falsch gelaufen ist:

# projectroot/tests/test_code.py
from module import code


def test_add__failing():
    assert code.add(10, 11) == 33

Ergebnisse:

$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items

tests/test_code.py F

======================================================== FAILURES ========================================================
___________________________________________________ test_add__failing ____________________________________________________

    def test_add__failing():
>       assert code.add(10, 11) == 33
E       assert 21 == 33
E        +  where 21 = <function add at 0x105d4d6e0>(10, 11)
E        +    where <function add at 0x105d4d6e0> = code.add

tests/test_code.py:5: AssertionError
================================================ 1 failed in 0.01 seconds ================================================

Einführung in Test-Fixtures

Für kompliziertere Tests müssen manchmal einige Dinge eingerichtet werden, bevor Sie den Code ausführen, den Sie testen möchten. Es ist möglich, dies in der Testfunktion selbst auszuführen, aber am Ende müssen Sie mit großen Testfunktionen so viel tun, dass es schwierig ist zu sagen, wo das Setup stoppt und der Test beginnt. Sie können auch eine Menge doppelten Setup-Codes zwischen Ihren verschiedenen Testfunktionen erhalten.

Unsere Code-Datei:

# projectroot/module/stuff.py
class Stuff(object):
    def prep(self):
        self.foo = 1
        self.bar = 2

Unsere Testdatei:

# projectroot/tests/test_stuff.py
import pytest
from module import stuff


def test_foo_updates():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    assert 1 == my_stuff.foo
    my_stuff.foo = 30000
    assert my_stuff.foo == 30000


def test_bar_updates():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    assert 2 == my_stuff.bar
    my_stuff.bar = 42
    assert 42 == my_stuff.bar

Dies sind ziemlich einfache Beispiele, aber wenn für unser Stuff Objekt mehr Setup erforderlich wäre, würde es schwerfällig werden. Wir haben festgestellt, dass zwischen unseren Testfällen ein paar doppelte Codes vorhanden sind. Lassen Sie uns den Code zuerst in eine separate Funktion umwandeln.

# projectroot/tests/test_stuff.py
import pytest
from module import stuff


def get_prepped_stuff():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    return my_stuff


def test_foo_updates():
    my_stuff = get_prepped_stuff()
    assert 1 == my_stuff.foo
    my_stuff.foo = 30000
    assert my_stuff.foo == 30000


def test_bar_updates():
    my_stuff = get_prepped_stuff()
    assert 2 == my_stuff.bar
    my_stuff.bar = 42
    assert 42 == my_stuff.bar

Das sieht zwar besser aus, aber wir haben immer noch den my_stuff = get_prepped_stuff() , der unsere Testfunktionen my_stuff = get_prepped_stuff() .

py.test Geräte zur Rettung!

Fixtures sind wesentlich leistungsfähigere und flexiblere Versionen von Testeinrichtungsfunktionen. Sie können viel mehr, als wir hier nutzen, aber wir gehen Schritt für Schritt vor.

Zuerst ändern wir get_prepped_stuff in ein Fixture namens prepped_stuff . Sie möchten Ihre Geräte mit Substantiven anstatt mit Verben benennen, da die Geräte später in den Testfunktionen selbst verwendet werden. Das @pytest.fixture gibt an, dass diese spezifische Funktion als Fixture und nicht als reguläre Funktion behandelt werden soll.

@pytest.fixture
def prepped_stuff():
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    return my_stuff

Jetzt sollten wir die Testfunktionen aktualisieren, damit sie das Gerät verwenden. Dazu fügen Sie ihrer Definition einen Parameter hinzu, der genau mit dem Namen des Geräts übereinstimmt. Wenn py.test ausgeführt wird, wird das Gerät vor dem Test ausgeführt und der Rückgabewert des Geräts wird durch diesen Parameter an die Testfunktion übergeben. (Beachten Sie, dass Vorrichtungen brauchen keinen Wert zurückgeben, sie anderen Setup Dinge tun können , statt wie eine externe Ressource aufrufen, die Dinge auf dem Dateisystem Arrangieren, setzen Werte in einer Datenbank, was die Tests müssen für die Einrichtung)

def test_foo_updates(prepped_stuff):
    my_stuff = prepped_stuff
    assert 1 == my_stuff.foo
    my_stuff.foo = 30000
    assert my_stuff.foo == 30000


def test_bar_updates(prepped_stuff):
    my_stuff = prepped_stuff
    assert 2 == my_stuff.bar
    my_stuff.bar = 42
    assert 42 == my_stuff.bar

Jetzt können Sie sehen, warum wir es mit einem Nomen benannt haben. aber die my_stuff = prepped_stuff Zeile ist ziemlich nutzlos, also verwenden prepped_stuff stattdessen direkt prepped_stuff .

def test_foo_updates(prepped_stuff):
    assert 1 == prepped_stuff.foo
    prepped_stuff.foo = 30000
    assert prepped_stuff.foo == 30000


def test_bar_updates(prepped_stuff):
    assert 2 == prepped_stuff.bar
    prepped_stuff.bar = 42
    assert 42 == prepped_stuff.bar

Jetzt benutzen wir Fixtures! Wir können weitergehen, indem wir den Umfang des Geräts ändern (dh es wird nur einmal pro Testmodul oder Testsuite-Ausführungssitzung statt nur einmal pro Testfunktion ausgeführt), indem Geräte erstellt werden, die andere Geräte verwenden, und das Gerät (und damit alle Geräte) Tests mit diesem Fixture werden mehrmals ausgeführt (einmal für jeden Parameter, der dem Fixture übergeben wird), Fixtures, die Werte aus dem Modul lesen, das sie aufruft ... Wie bereits erwähnt, haben Fixtures viel mehr Leistung und Flexibilität als eine normale Setup-Funktion.

Nach den Tests aufräumen.

Nehmen wir an, unser Code ist gewachsen und unser Stuff-Objekt muss jetzt besonders aufgeräumt werden.

# projectroot/module/stuff.py
class Stuff(object):
def prep(self):
    self.foo = 1
    self.bar = 2

def finish(self):
    self.foo = 0
    self.bar = 0

Wir könnten etwas Code hinzufügen, um die Aufräumaktion am Ende jeder Testfunktion aufzurufen. Fixtures bieten jedoch eine bessere Möglichkeit, dies zu tun. Wenn Sie dem Gerät eine Funktion hinzufügen und es als Finalizer registrieren, wird der Code in der Finalizer-Funktion aufgerufen, nachdem der Test mit dem Gerät durchgeführt wurde. Wenn der Umfang des Fixtures größer ist als eine einzelne Funktion (wie ein Modul oder eine Sitzung), wird der Finalizer ausgeführt, nachdem alle Tests im Umfang abgeschlossen sind. Nach Abschluss des Moduls oder am Ende der gesamten Testlaufsitzung .

@pytest.fixture
def prepped_stuff(request):  # we need to pass in the request to use finalizers
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    def fin():  # finalizer function
        # do all the cleanup here
        my_stuff.finish()
    request.addfinalizer(fin)  # register fin() as a finalizer
    # you can do more setup here if you really want to
    return my_stuff

Die Verwendung der Finalizer-Funktion innerhalb einer Funktion kann auf den ersten Blick etwas schwer zu verstehen sein, insbesondere bei komplizierteren Geräten. Sie können stattdessen ein Ertrags-Fixture verwenden , um dasselbe mit einem besser lesbaren Ausführungsablauf zu tun. Der einzige wirkliche Unterschied besteht darin, dass wir anstelle von return eine yield für den Teil des Geräts verwenden, in dem das Setup durchgeführt wird und die Steuerung zu einer Testfunktion gehen sollte, und dann den gesamten Bereinigungscode nach der yield hinzufügen. Wir dekorieren es auch als yield_fixture damit py.test damit umgehen kann.

@pytest.yield_fixture
def prepped_stuff():  # it doesn't need request now!
    # do setup
    my_stuff = stuff.Stuff()
    my_stuff.prep()
    # setup is done, pass control to the test functions
    yield my_stuff
    # do cleanup 
    my_stuff.finish()

Und damit ist das Intro zu Test Fixtures abgeschlossen!

Weitere Informationen finden Sie in der offiziellen Dokumentation des py.test-Geräts und in der Dokumentation des offiziellen Ertragsgeräts



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow