Wie richte ich meine Pytest-Klasse mit Tests richtig ein und aus?


100

Ich verwende Selen für End-to-End-Tests und kann nicht verstehen, wie setup_classund teardown_classMethoden verwendet werden.

Ich muss den Browser in der setup_classMethode einrichten , dann eine Reihe von Tests durchführen, die als Klassenmethoden definiert sind, und schließlich den Browser in der teardown_classMethode beenden .

Aber logischerweise scheint es eine schlechte Lösung zu sein, denn tatsächlich funktionieren meine Tests nicht mit der Klasse, sondern mit dem Objekt. Ich übergebe selfparam in jeder Testmethode, damit ich auf die Variablen der Objekte zugreifen kann:

class TestClass:
  
    def setup_class(cls):
        pass
        
    def test_buttons(self, data):
        # self.$attribute can be used, but not cls.$attribute?  
        pass
        
    def test_buttons2(self, data):
        # self.$attribute can be used, but not cls.$attribute?
        pass
        
    def teardown_class(cls):
        pass
    

Und es scheint sogar nicht richtig zu sein, eine Browserinstanz für eine Klasse zu erstellen. Sie sollte für jedes Objekt separat erstellt werden, oder?

Also muss ich __init__und __del__Methoden anstelle von setup_classund verwenden teardown_class?

Antworten:


93

Laut Fixture Finalization / Executing Teardown Code besteht die derzeitige Best Practice für Setup und Teardown darin, yieldanstelle von return:

import pytest

@pytest.fixture()
def resource():
    print("setup")
    yield "resource"
    print("teardown")

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Das Ausführen führt zu

$ py.test --capture=no pytest_yield.py
=== test session starts ===
platform darwin -- Python 2.7.10, pytest-3.0.2, py-1.4.31, pluggy-0.3.1
collected 1 items

pytest_yield.py setup
testing resource
.teardown


=== 1 passed in 0.01 seconds ===

Eine andere Möglichkeit, Teardown-Code zu schreiben, besteht darin, ein request-context-Objekt in Ihre Fixture-Funktion aufzunehmen und seine request.addfinalizerMethode mit einer Funktion aufzurufen, die den Teardown ein- oder mehrmals ausführt:

import pytest

@pytest.fixture()
def resource(request):
    print("setup")

    def teardown():
        print("teardown")
    request.addfinalizer(teardown)
    
    return "resource"

class TestResource:
    def test_that_depends_on_resource(self, resource):
        print("testing {}".format(resource))

Sie kopieren dies also in jede Testdatei, für die Sie die Ressource benötigen?
Andy Hayden

@AndyHayden Abhängig davon, wie Sie Ihre Geräte schreiben, können Sie sie in jede Testdatei einfügen, in der Sie sie benötigen, oder in eine conftest.py-Datei. Stackoverflow.com/questions/34466027/…
Everett Toews

2
Dies ist jedoch kein Klassenaufbau, oder? Es würde vor jeder Testmethode in der Klasse ausgeführt.
Malhar

1
In diesem speziellen Fall wird es nur ausgeführt, wenn es als Parameter in einer Testmethode verwendet wird. zB der resourceParameter intest_that_depends_on_resource(self, resource)
Everett Toews

64

Meinen Sie beim Schreiben von "als Klassenmethoden definierten Tests" wirklich Klassenmethoden (Methoden, die ihre Klasse als ersten Parameter erhalten) oder nur reguläre Methoden (Methoden, die eine Instanz als ersten Parameter erhalten)?

Da Ihr Beispiel selffür die Testmethoden verwendet wird, gehe ich von letzterem aus, sodass Sie setup_methodstattdessen nur Folgendes verwenden müssen :

class Test:

    def setup_method(self, test_method):
        # configure self.attribute

    def teardown_method(self, test_method):
        # tear down self.attribute

    def test_buttons(self):
        # use self.attribute for test

Die Testmethodeninstanz wird an setup_methodund übergeben teardown_method, kann jedoch ignoriert werden, wenn Ihr Setup- / Teardown-Code den Testkontext nicht kennen muss. Weitere Informationen finden Sie hier .

Ich empfehle Ihnen auch, sich mit den Leuchten von py.test vertraut zu machen , da diese ein leistungsfähigeres Konzept darstellen.


1
Fixtures sind schwächer als Klassenmethoden: Sie erlauben keine Zerstörung von Objekten, die nicht von ihnen erstellt wurden (was oft wirklich notwendig ist). Ansonsten vielen Dank für Informationen.
wvxvw

Dies traf mich beim Upgrade einer Codebasis von einer 3.0.x-Version von pytest auf eine 4.x-Variante. Einige ältere Codes, die setup_classmit verspotteten Methoden und dergleichen verwendet wurden, mussten modernisiert werden. setup_class(self, foo, bar)->setup_method(self,function,foo,bar)
jxramos

28

Dies könnte http://docs.pytest.org/en/latest/xunit_setup.html helfen

In meiner Testsuite gruppiere ich meine Testfälle in Klassen. Für die Einrichtung und den Abbau, die ich für alle Testfälle in dieser Klasse benötige, verwende ich die Methoden setup_class(cls)und teardown_class(cls).

Und für die Einrichtung und den Abbau, die ich für jeden Testfall benötige, verwende ich das setup_method(method)undteardown_method(methods)

Beispiel:

lh = <got log handler from logger module>

class TestClass:
    @classmethod
    def setup_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    @classmethod
    def teardown_class(cls):
        lh.info("starting class: {} execution".format(cls.__name__))

    def setup_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def teardown_method(self, method):
        lh.info("starting execution of tc: {}".format(method.__name__))

    def test_tc1(self):
        <tc_content>
        assert 

    def test_tc2(self):
        <tc_content>
        assert

Wenn ich jetzt meine Tests ausführe und die TestClass-Ausführung startet, werden die Details protokolliert, wann die Ausführung beginnt, wann die Ausführung beendet wird und dasselbe für die Methoden.

Sie können weitere Setup- und Teardown-Schritte an den jeweiligen Standorten hinzufügen.

Ich hoffe es hilft!


Hallo @Kiran, was ist der Unterschied zwischen dem setup_classvs setup_method?
Imsrgadich

1
@imsrgadich Wenn Sie Ihre Testfälle in Klassen organisieren, wird <Setup / Teardown> _class für die Setup- und Teardown-Schritte der Klasse verwendet, und <Setup / Teardown> _method sind die jeweiligen Schritte für jede Testfallmethode.
Kiran Vemuri

1
Verdammt ... jetzt verstehe ich es! war für ein paar Stunden drauf. Also, um die Dinge ins rechte Licht zu rücken. Das <setup/teardown>_classfür die ganze Klasse. Hier können Dinge wie das Setzen eines Links zur Datenbank oder das Laden der Datendatei geschehen. Und dann kann jeder Testfall sein eigenes Setup in Form von haben <setup/teardown>_method. Die Dinge sind jetzt viel klarer. Vielen Dank!
Imsrgadich

24

Wie @Bruno vorgeschlagen hat, ist die Verwendung von Pytest-Vorrichtungen eine weitere Lösung, auf die beide Testklassen oder auch nur einfache Testfunktionen zugreifen können. Hier ist ein Beispiel zum Testen von Python2.7-Funktionen :

import pytest

@pytest.fixture(scope='function')
def some_resource(request):
    stuff_i_setup = ["I setup"]

    def some_teardown():
        stuff_i_setup[0] += " ... but now I'm torn down..."
        print stuff_i_setup[0]
    request.addfinalizer(some_teardown)

    return stuff_i_setup[0]

def test_1_that_needs_resource(some_resource):
    print some_resource + "... and now I'm testing things..."

Laufen test_1...erzeugt also:

I setup... and now I'm testing things...
I setup ... but now I'm torn down...

Beachten Sie, dass stuff_i_setupauf das Gerät verwiesen wird, damit dieses Objekt sein kann setupund torn downfür den Test, mit dem es interagiert. Sie können sich vorstellen, dass dies für ein beständiges Objekt nützlich sein könnte, z. B. eine hypothetische Datenbank oder eine Verbindung, die vor jedem Testlauf gelöscht werden muss, um sie isoliert zu halten.


13

Ihr Code sollte genau so funktionieren, wie Sie es erwarten, wenn Sie @classmethodDekorateure hinzufügen .

@classmethod 
def setup_class(cls):
    "Runs once per class"

@classmethod 
def teardown_class(cls):
    "Runs at end of class"

Siehe http://pythontesting.net/framework/pytest/pytest-xunit-style-fixtures/


Dies ist ziemlich genau das, was in der Dokumentation erscheint. Das Problem, das ich mit dem Dokument hatte, war, dass ich Schwierigkeiten hatte, den Kontext zu verstehen: Selbst wird traditionell als Selbst bezeichnet, nicht als cls, daher schien mir dies außerhalb des Kontexts der Klasse selbst seltsam. Kiran (oben) liefert diesen Kontext.
Cognitiaclaeves

1
@Cognitiaclaeves "self wird traditionell als self bezeichnet, nicht als cls" Ja, selfwird beispielsweise für Methoden verwendet, wobei das erste Argument die spezifische Objektinstanz ist, auf der die Methodenoperation stattfindet, während clsfür @classmethods verwendet wird, an die gebunden ist die Klasse und keine Instanz der Klasse (dh ein Objekt).
code_dredd
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.