Python Language
py.test
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