Python Language
py.test
Sök…
Ställa in py.test
py.test
är ett av flera py.test
från tredje part som är tillgängliga för Python. Det kan installeras med pip
med
pip install pytest
Koden som ska testas
Säg att vi testar en tilläggsfunktion i projectroot/module/code.py
:
# projectroot/module/code.py
def add(a, b):
return a + b
Testkoden
Vi skapar en testfil i projectroot/tests/test_code.py
. Filen måste börja med test_
för att erkännas som en testfil.
# projectroot/tests/test_code.py
from module import code
def test_add():
assert code.add(1, 2) == 3
Kör testet
Från projectroot
kör vi helt enkelt 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 ================================================
Misslyckade test
Ett misslyckande test ger bra resultat om vad som gick fel:
# projectroot/tests/test_code.py
from module import code
def test_add__failing():
assert code.add(10, 11) == 33
Resultat:
$ 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 ================================================
Introduktion till testfixturer
Mer komplicerade tester behöver ibland ha saker inställda innan du kör koden du vill testa. Det är möjligt att göra detta i själva testfunktionen, men då hamnar du med stora testfunktioner som gör så mycket att det är svårt att se var installationen slutar och testet börjar. Du kan också få en hel del duplicerad installationskod mellan dina olika testfunktioner.
Vår kodfil:
# projectroot/module/stuff.py
class Stuff(object):
def prep(self):
self.foo = 1
self.bar = 2
Vår testfil:
# 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
Dessa är ganska enkla exempel, men om vårt Stuff
objekt behövde mycket mer installation, skulle det bli svårt. Vi ser att det finns en del duplicerad kod mellan våra testfall, så låt oss refaktorera det till en separat funktion först.
# 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
Det här ser bättre ut, men vi har fortfarande my_stuff = get_prepped_stuff()
-samtalet med våra testfunktioner.
py.test fixturer till undsättning!
Fixturer är mycket kraftfullare och flexibla versioner av testinställningsfunktioner. De kan göra mycket mer än vi utnyttjar här, men vi tar det ett steg i taget.
Först byter vi get_prepped_stuff
till en fixtur som kallas prepped_stuff
. Du vill namnge dina fixturer med substantiv snarare än verb på grund av hur fixturerna kommer att hamna i testfunktionerna senare. @pytest.fixture
indikerar att den här specifika funktionen bör hanteras som en fixtur snarare än en vanlig funktion.
@pytest.fixture
def prepped_stuff():
my_stuff = stuff.Stuff()
my_stuff.prep()
return my_stuff
Nu bör vi uppdatera testfunktionerna så att de använder fixturen. Detta görs genom att lägga till en parameter till deras definition som exakt matchar fixturnamnet. När py.test körs kommer den att köra fixturen innan testet körs och sedan överföra fixturets returvärde till testfunktionen genom den parametern. (Observera att fixturer inte behöver returnera ett värde; de kan göra andra konfigurationssaker istället, som att ringa en extern resurs, ordna saker i filsystemet, lägga värden i en databas, oavsett testens behov för installation)
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
Nu kan du se varför vi namngav det med ett substantiv. men my_stuff = prepped_stuff
linjen är ganska mycket värdelös, så låt oss bara använda prepped_stuff
direkt istället.
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
Nu använder vi fixturer! Vi kan gå längre genom att ändra fixturens räckvidd (så att den bara körs en gång per testmodul eller testsuits exekveringssession istället för en gång per testfunktion), bygga fixturer som använder andra fixturer, parametrera fixturen (så att fixturen och alla test som använder den fixturen körs flera gånger, en gång för varje parameter som ges till fixturen), fixturer som läser värden från modulen som kallar dem ... som nämnts tidigare har fixturer mycket mer kraft och flexibilitet än en normal installationsfunktion.
Rengöring efter testen är klar.
Låt oss säga att vår kod har vuxit och vårt Stuff-objekt behöver nu speciell sanering.
# 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
Vi kan lägga till en kod för att kalla upp saneringen längst ner i varje testfunktion, men fixturer ger ett bättre sätt att göra detta. Om du lägger till en funktion i fixturen och registrerar den som en finaliserare , kommer koden i finaliseringsfunktionen att ringas efter testet med fixturen har gjort. Om fixturens räckvidd är större än en enda funktion (som modul eller session), kommer finaliseraren att utföras efter att alla tester i omfattning är avslutade, så efter att modulen är klar eller i slutet av hela testkörningssessionen .
@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
Att använda finaliseringsfunktionen i en funktion kan vara lite svårt att förstå vid första anblicken, särskilt när du har mer komplicerade fixturer. Du kan istället använda en avkastningsarmatur för att göra samma sak med ett mänskligt läsbart exekveringsflöde. Den enda verkliga skillnaden är att i stället för att använda return
använder vi ett yield
vid den del av fixturen där installationen är klar och kontrollen ska gå till en testfunktion, lägg sedan till all saneringskod efter yield
. Vi dekorerar det också som ett yield_fixture
så att py.test vet hur det ska hanteras.
@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()
Och det avslutar Intro to Test Fixtures!
Mer information finns i den officiella dokumentationen för py.test-fixtur och den officiella dokumentationen för avkastningsarmatur