pytest `TestClass` Verwendung unklar

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo,

ich finde einfach keinen richtigen Zugang zum Schreiben von Tests... :( Zum Testen meiner recurrence-Klassen habe ich bisher folgendes verwendet:

Code: Alles auswählen

import sys

sys.path.append('/home/mutetella/Daten/denkdran/src')
from dt import DateTimeScope as dts
from recurrence import (
    DailyRecurrence as dr, WeeklyRecurrence as wr,
    MonthlyRecurrence as mr, YearlyRecurrence as yr
)

def check(recurrence, expected, results):
    assert len(expected) == len(results)
    for result, expected in zip(results, expected):
        assert result == expected

def test_daily_01():
    b = dts((2014, 3, 31), ())
    v = ((2014, 3, 31), (9999, 12, 31))
    q = dts((2014, 3, 24), (2014, 4, 14))
    tests = (
        (dr(base=b, validity=v, interval=1, count=None),
         (dts((2014, 3, 31), ()), dts((2014, 4, 1), ()),
          dts((2014, 4, 2), ()), dts((2014, 4, 3), ()),
          dts((2014, 4, 4), ()), dts((2014, 4, 5), ()),
          dts((2014, 4, 6), ()), dts((2014, 4, 7), ()),
          dts((2014, 4, 8), ()), dts((2014, 4, 9), ()),
          dts((2014, 4, 10), ()), dts((2014, 4, 11), ()),
          dts((2014, 4, 12), ()), dts((2014, 4, 13), ()),
          dts((2014, 4, 14), ())
         )
        ),
        (dr(base=b, validity=v, interval=2, count=None),
         (dts((2014, 3, 31), ()), dts((2014, 4, 2), ()),
          dts((2014, 4, 4), ()), dts((2014, 4, 6), ()),
          dts((2014, 4, 8), ()), dts((2014, 4, 10), ()),
          dts((2014, 4, 12), ()), dts((2014, 4, 14), ()),
         )
        ),
        (dr(base=b, validity=v, interval=3, count=None),
         (dts((2014, 3, 31), ()), dts((2014, 4, 3), ()),
          dts((2014, 4, 6), ()), dts((2014, 4, 9), ()),
          dts((2014, 4, 12), ())
         )
        )
    )
    for recurrence, expected in tests:
        results = []
        for query in q:
            for result in recurrence._query(query):
                results.append(result)
        yield check, recurrence, expected, results
`tests` hat noch weitere Werte für verschiedene `interval`/`count` Kombinationen, die ich der Übersicht wegen weggelassen habe. Für die verschiedenen `validity`/`base` Kombinationen, die ich ebenfalls testen möchte, habe ich weitere `test_daily_XX()` erstellt.

Jetzt suche ich nach einer Möglichkeit, einen Test zu schreiben, dem ich die verschiedenen Testbedingungen (`dr`, `q`ueries und `expected`) übergebe. Bis jetzt habe ich das:

Code: Alles auswählen

class TestDaily(object):
    def setup_class(self):
        self.dr = dr(base=dts((2014, 3, 31), ()),
                     validity=((2014, 3, 31), (9999, 12, 31)),
                     interval=1, count=None)
        self.queries = dts((2014, 3, 24), (2014, 4, 14))
        self.expected = (
            dts((2014, 3, 31), ()), dts((2014, 4, 1), ()),
            dts((2014, 4, 2), ()), dts((2014, 4, 3), ()),
            dts((2014, 4, 4), ()), dts((2014, 4, 5), ()),
            dts((2014, 4, 6), ()), dts((2014, 4, 7), ()),
            dts((2014, 4, 8), ()), dts((2014, 4, 9), ()),
            dts((2014, 4, 10), ()), dts((2014, 4, 11), ()),
            dts((2014, 4, 12), ()), dts((2014, 4, 13), ()),
            dts((2014, 4, 14), ())
        )

    def get_results(self):
        for query in self.queries:
            for result in self.dr._query(query):
                yield result

    def check(self, expected, result):
        assert expected == result

    def test(self):
        results = tuple(self.get_results())
        assert len(self.expected) == len(results)
        for expected, result in zip(self.expected, results):
            yield self.check, expected, result
Nur: Wie kann ich eine solche Klasse mit verschiedenen Werten (`self.dr`, `self.queries`, `self.expected`) "füttern"? Oder sollte ich für jedes Szenario eine eigene Klasse erstellen?

Ich weiß einfach nicht, wie ich da 'rangehen soll! Das, was ich testen möchte, ist ja immer dasselbe, nur die verschiedenen Szenarien und daraus folgend die vielen zu testenden Resultate bekomme ich nicht strukturiert!

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Ich würde mir an Deiner Stelle unittest und unittest.TestCase anschauen.

Du leitest Deine Test-Klasse von unittest.TestCase ab. Jede Methode mit dem Prefix test ist ein eigener abgschlossener und unabhängiger Testfall. Mit unittest.TestSuite kannst Du Deine Test-Klassen gruppieren.
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@mutetella: Eine Testklasse testet normalerweise einen bestimmten Bereich in deinem Programm - eben eine Unit (Einheit). Die Methoden innerhalb einer solchen Klassen sollten ihrerseits Namen tragen, die den Test näher beschreiben. Etwa: ``test_negative_values()``, ``test_empty_values()``, ``test_end_date_before_start_date()``, usw. Und da packst du halt dann 2-3 Testfälle rein (je nach Kontext) und rufst die vom Test-Framework dafür vorgesehene Funktionalität auf. Beim schon vorgeschlagenen ``unittest``-Framework hat man jeweils passende Methoden (etwa: "assertEqual", "assertIn", "assertIs", "assertRaises"), denen man jeweils das tatsächliche Ergebnis und das erwartete Ergebnis (oder die erwartete Exception-Klasse) übergibt. ``pytest`` regelt das offenbar über simple ``assert``-Statements. Ich kenne dieses Framework nicht genauer. Hilft dir das bereits weiter?
BlackJack

@mutetella: Wozu brauchst Du denn die Klasse? Und vielleicht solltest Du die `test_*()`-Funktionen sinnvoller benennen, damit man weiss *was* die testen.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

MagBen hat geschrieben:Ich würde mir an Deiner Stelle unittest [...] anschauen.
Warum bzw. warum nicht `pytest`?
snafu hat geschrieben:Hilft dir das bereits weiter?
Insofern, dass es meinen Eindruck bestärkt, dass eine Testklasse, die letztlich nur die `_query()`-Methode testet, keinen Mehrwert gegenüber einer Testfunktion bringt.
BlackJack hat geschrieben:Wozu brauchst Du denn die Klasse?
Genau darauf finde ich keine Antwort... Du denkst also, dass in meinem Fall die Lösung über Testfunktionen, die jeweils ein bestimmtes Wiederholungsszenario abfragen, in Ordnung geht?
BlackJack hat geschrieben:... solltest Du die `test_*()`-Funktionen sinnvoller benennen, ...
Das hab' ich über Kommentare gemacht. Grundsätzlich versuche ich zwar, Namen zu verwenden, die einen Kommentar überflüssig machen. Aber das ist mir beim Testen einfach noch nicht gelungen... :roll:

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Testklassen finde ich erst dann nützlich wenn sich die Tests darin einen gemeinsamen Zustand teilen, oder zu viele Daten die man sonst immer übergeben müsste. Das mit dem Zustand sollte man sowieso vermeiden, aber manchmal gibt es halt ”teurere” Setups wie das herstellen einer Datenbankverbindung oder das erstellen von umfangreicheren Testdaten was man nur einmal für eine Reihe von Tests machen möchte, statt für jeden Test auf's neue.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

mutetella hat geschrieben:Warum bzw. warum nicht `pytest`?
Ich kenne pytest nicht, deshalb würde ich Dir auch nie empfehlen es nicht zu nehmen.
Ich habe in Deinem Code aber nichts pytest Spezifisches gesehn (vielleicht braucht man das bei pytest auch nicht?).

Ich habe den Eindruck, dass Du Dir nicht sicher warst, wie Du Deine Testfälle strukturieren sollst. unittest schreibt Dir eine gewisse Struktur vor, allein das könnte Dir schon weiterhelfen.
mutetella hat geschrieben:Insofern, dass es meinen Eindruck bestärkt, dass eine Testklasse, die letztlich nur die `_query()`-Methode testet, keinen Mehrwert gegenüber einer Testfunktion bringt.
Die Testklasse gruppiert Deine Test-Funktionen und dient dazu die Test-Funktionen an das Test-Framework zu übergeben. Natürlich kann man auf Klassen verzichten, aber damit verzichtest Du auf eine Strukturierungsmöglichkeit und unittest schreibt diese Struktur sogar vor.
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

mutetella hat geschrieben:warum nicht `pytest`?
Schau Dir auf jeden Fall auch pytest an. Lt. Deiner Signatur begibst Du Dich vermutlich zur EuroPython. Dort wird es einiges zu dem Thema geben.
BlackJack

@kbr: mutetella benutzt bereits pytest. ;-)

@MagBen: Pytest verwendet hauptsächlich die ``assert``-Anweisung und Namenskonventionen. Darum sieht man da über weite Strecken von Testcode oft nichts Spezifisches. Statt Klassen als Namensräume zu ”missbauchen” verwende ich Module zum organisieren der Tests. (Ich verwende eher `nose`, aber der Grundansatz da keinen javatypischen Boilerplate um Testfälle zu stricken und ``assert`` zu verwenden, findet man auch bei `nose`, und pytest kann auch `nose`-Testfälle verarbeiten.)
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@BlackJack: Das heißt, auch zum Testen einer Klasse würdest du für jede Klasse ein eigenes Testmodul mit passenden Testfunktionen für diese Klasse anlegen, oder würdest du *dann* auch Testklassen benutzen?
BlackJack

@snafu: Wie gesagt Klassen benutze ich nicht einfach als Namensraum. Allerdings würde ich auch nicht zwingend für jede Klasse ein neues Modul erstellen, anderseits aber auch mal mehr als ein Modul für eine Klasse. Das kommt immer darauf an wie umfangreich die Tests sind und ob ich die thematisch sinnvoll gruppieren kann. Und ich schreibe zu wenig Tests. :-)
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

BlackJack hat geschrieben:@kbr: mutetella benutzt bereits pytest. ;-)
Oops, zu schnell gelesen ...
BlackJack hat geschrieben:Und ich schreibe zu wenig Tests. :-)
... aber das habe ich genau gelesen ... :D
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ich beschäftige und verwende Tests erst seit wenigen Wochen. Sie haben mein Leben verändert!!! :mrgreen:
Seitdem ich versuche, das, was ich schreiben oder umschreiben möchte, zuerst mit einem dazugehörigen Test zu versehen, hab' ich einen richtigen Motivationsschub bekommen.
Bisher hatte ich nach der Fertigstellung einer Klasse oder Funktion schnell mal ein paar händische Tests gemacht und fertig. Nachdem ich dann kürzlich seit längerem wieder einmal meine Wiederholungsklassen "in die Hand genommen" hatte, spuckten mir diese einfach nicht das aus, was ich erwartete. Sehr frustrierend! Mit der einen Wertekombination funktionierte es, mit einer anderen dann wieder doch nicht usw.
Also fing ich an, Tests zu schreiben. Schon beim Nachdenken darüber, was ich denn eigentlich alles für Ergebnisse erwarte, wurde mir klar, dass ich nie wieder ohne Tests arbeiten darf. Heute, da ich zumindest für meine Wiederholungsklassen Tests geschrieben habe, tappe ich nicht mehr bei jeder Änderung oder Implementierung im Dunkeln, sondern sehe nach einem Testdurchlauf gleich, ob mein Code trägt oder in einem speziellen Fall (meistens an den Rändern) doch einstürzt! Das ist sehr sehr geil!

Ok, das wollte ich dann doch noch gesagt haben, auch wenn ihr auf einem ganz anderen Niveau arbeitet und euch so ein Kram wohl nicht mehr so vom Hocker reißen wird... :D

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten