Zoeken…


Py.test instellen

py.test is een van de verschillende py.test van derden die beschikbaar zijn voor Python. Het kan worden geïnstalleerd met behulp van pip with

pip install pytest

De te testen code

Stel dat we een projectroot/module/code.py in projectroot/module/code.py :

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

De testcode

We maken een testbestand in projectroot/tests/test_code.py . Het bestand moet beginnen met test_ om te worden herkend als een test_ .

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


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

De test uitvoeren

Vanuit projectroot we eenvoudig 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 ================================================

Niet-uitgevoerde tests

Een mislukte test levert nuttige resultaten op over wat er mis is gegaan:

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


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

resultaten:

$ 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 ================================================

Inleiding tot het testen van armaturen

Bij meer gecompliceerde tests moeten soms dingen zijn ingesteld voordat u de code uitvoert die u wilt testen. Het is mogelijk om dit in de testfunctie zelf te doen, maar dan eindigen er grote testfuncties die zo veel doen dat het moeilijk is om te zeggen waar de installatie stopt en de test begint. U kunt ook veel dubbele installatiecode krijgen tussen uw verschillende testfuncties.

Ons codebestand:

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

Ons testbestand:

# 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

Dit zijn vrij eenvoudige voorbeelden, maar als ons Stuff object veel meer configuratie nodig had, zou het log worden. We zien dat er een dubbele code tussen onze testgevallen zit, dus laten we dat eerst in een afzonderlijke functie veranderen.

# 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

Dit ziet er beter uit, maar we hebben nog steeds de my_stuff = get_prepped_stuff() aanroep die onze testfuncties my_stuff = get_prepped_stuff() .

py.test armaturen te hulp!

Armaturen zijn veel krachtigere en flexibelere versies van testinstellingen. Ze kunnen veel meer dan we hier doen, maar we doen het stap voor stap.

Eerst veranderen we get_prepped_stuff in een fixture genaamd prepped_stuff . Je wilt je fixtures een naam geven met zelfstandige naamwoorden in plaats van werkwoorden vanwege de manier waarop de fixtures later in de testfuncties zelf worden gebruikt. Het @pytest.fixture geeft aan dat deze specifieke functie als een fixture moet worden behandeld in plaats van als een normale functie.

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

Nu moeten we de testfuncties bijwerken zodat ze het armatuur gebruiken. Dit wordt gedaan door een parameter aan hun definitie toe te voegen die exact overeenkomt met de naam van het toestel. Wanneer py.test wordt uitgevoerd, zal het de fixture uitvoeren voordat de test wordt uitgevoerd en vervolgens de retourwaarde van de fixture via die parameter in de testfunctie doorgeven. (Houd er rekening mee dat fixtures geen waarde moeten retourneren; ze kunnen in plaats daarvan andere setup-dingen doen, zoals het aanroepen van een externe bron, het regelen van dingen op het bestandssysteem, het plaatsen van waarden in een database, wat de tests ook nodig hebben voor de setup)

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 kun je zien waarom we het een zelfstandig naamwoord hebben gegeven. maar de my_stuff = prepped_stuff is vrijwel nutteloos, dus laten we in plaats daarvan gewoon prepped_stuff gebruiken.

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 gebruiken we armaturen! We kunnen verder gaan door de reikwijdte van de fixture te wijzigen (zodat deze slechts één keer per testmodule of testsuite wordt uitgevoerd in plaats van één keer per testfunctie), fixtures bouwen die andere fixtures gebruiken, de fixture parametriseren (zodat de fixture en alle tests met die fixture worden meerdere keren uitgevoerd, eenmaal voor elke parameter die aan de fixture wordt gegeven), fixtures die waarden lezen van de module die ze oproept ... zoals eerder vermeld, hebben fixtures veel meer kracht en flexibiliteit dan een normale setup-functie.

Opruimen nadat de tests zijn voltooid.

Laten we zeggen dat onze code is gegroeid en ons Stuff-object nu moet worden opgeschoond.

# 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

We kunnen wat code toevoegen om het opschonen onderaan elke testfunctie aan te roepen, maar armaturen bieden een betere manier om dit te doen. Als u een functie aan de fixture toevoegt en deze registreert als finalizer , wordt de code in de finalizerfunctie opgeroepen nadat de test met de fixture is voltooid. Als de scope van de fixture groter is dan een enkele functie (zoals module of sessie), wordt de finalizer uitgevoerd nadat alle tests in scope zijn voltooid, dus nadat de module is uitgevoerd of aan het einde van de hele testrunsessie .

@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

Het gebruik van de finalizer-functie in een functie kan op het eerste gezicht wat moeilijk te begrijpen zijn, vooral als u meer gecompliceerde armaturen hebt. U kunt in plaats daarvan een opbrengstvoorziening gebruiken om hetzelfde te doen met een meer leesbare uitvoeringsstroom. Het enige echte verschil is dat we in plaats van return een yield op het gedeelte van de fixture waar de installatie is voltooid en de besturing naar een testfunctie moet gaan en vervolgens alle opruimcode na de yield . We decoreren het ook als een yield_fixture zodat py.test weet hoe het moet worden verwerkt.

@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()

En daarmee is de inleiding tot het testen van armaturen afgesloten!

Voor meer informatie, zie de officiële py.test fixture documentatie en de officiële yield fixture documentatie



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow