Seite 1 von 1
TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 13:27
von akis.kapo
Hi all,
folgendes Szenario:
Ich hab ne eigene TestCase Klasse und ich habe mehrere Funktionen darin definiert,
jedoch stellt nicht jede Funktion einen eigenen, validen TestCase dar.
In meiner TestKlasse hab ich ganz unten stehen:
Code: Alles auswählen
if __name__ == '__main__':
def suite():
tests = [
'moonlight',
'sunshine'
]
suite = unittest.TestSuite(map(MyTestCase, tests))
return suite
unittest.TextTestRunner().run(suite())
Das habe ich von der Stdlib auf
http://docs.python.org/2/library/unittest.html.
Mir gefällt dieses Konstrukt aber nicht und ich will, dass meine TestKlasse selber sagt,
welche die validen TestCases (Funktionen) sind.
Wie ich mir das vorgestellt habe (aber es nicht funktioniert!), seht ihr hier:
Code: Alles auswählen
class MyTestCase(unittest.TestCase):
TestCases = set()
def __init__(self):
unittest.TestCase.__init__(self)
for i in dir(self):
if i.startswith('TestCase_'):
self.TestCases.add(i)
def TestCase_sunshine():
pass # run this specific TestCase
def TestCase_moonlight():
pass # run this specific TestCase
def others():
pass # not a real testcase, might be shared/used by real TestCases...
Wie schon erwähnt, das funktioniert so nicht ganz (TypeError Exception in der __init__()).
Verwenden wollen würde ich es so, zumindest stell ich es mir so vor...:
Code: Alles auswählen
if __name__ == '__main__':
def suite():
# tests = [
# 'moonlight',
# 'sunshine'
# ]
suite = unittest.TestSuite(map(MyTestCase, MyTestCase.TestCases))
return suite
unittest.TextTestRunner().run(suite())
Und wer weiß, möglicherweise gibt's da ne komplett andere Lösung, wie man seine (echten) TestCases in einer TestKlasse von (misc) Funktionen unterscheiden und automatisiert "inventorisieren" kann.
Ich will nicht manuell angeben, welche TestCases es gibt und ich will
das MyTestCase.TestCases set() auch nicht manuell ausfüllen, das soll automatisch befüllt werden "as I code along".
Wer kann 'was dazu sagen? Ist's überhaupt möglich (denke schon)?
Vielen Dank für eure Antworten & Ideen im Voraus.
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 13:32
von EyDu
Hallo,
du solltst dir vielleicht noch einmal die Dokumentation zum unittest-Modul anschauen, dieses hat nämlich schon das gewünschte Verhalten

Interessant ist für dich wahrscheinlich
dieser Abschnitt.
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 13:39
von akis.kapo
Oww...
Whoopsy...
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 17:14
von akis.kapo
Sorry, dass ich nochmal nachhake, aber mal abgesehen davon, dass es wohl in unittest für alle TestCases mit "test_" implementiert ist,
kann ich vielleicht trotzdem ne Antwort haben für "allgemein". Muss ja nicht zwangsweise ne Testklasse sein, sondern darf auch komplett custom sein.
Code: Alles auswählen
class Horses:
Staples = set()
def __init__(self):
for i in dir(self):
if i.startswith('Staple_'):
self.Staples.add(i)
def Staples_north(self):
pass
def Staples_south(self):
pass
def clean_staple(self, staple):
pass
Obiger Code produziert aber nicht das erwünschte Ergebnis:
Das set() ist leer, die Klasse initialisiert/befüllt das set() nicht, wie ich es in __init__() vorgegeben habe.
Damit der Thread nicht für die Katz ist, könnten wir bitte hier weitermachen?
EDIT: ach ja, Staples ist bewusst auf der Klassenebene definiert und nicht auf Objektebene (self.Staples...), falls sich das jemand fragt.
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 17:17
von anogayales
probier mal:
Code: Alles auswählen
class Horses:
def __init__(self):
self.Staples = set()
for i in dir(self):
if i.startswith('Staple_'):
self.Staples.add(i)
Wenn du in der __init__ Methode wirklich auf das Klassenattribut zugreifen willst, so musst du das auch explizit machen:
Code: Alles auswählen
class Horses:
Staples = set()
def __init__(self):
for i in dir(self):
if i.startswith('Staple_'):
Horses.Staples.add(i)
Stichwort: Klassenattribut vs. Instanzattritbut
Ein Pferdestall ist übrigens ein stable und kein staple. Außerdem solltest du dich in der Namensgebung an PEP8 halten, z.B. sollte Staples klein geschrieben werden, also staples bzw. stables.
Grüße,
anogayales
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 17:22
von BlackJack
@akis.kapo:
Code: Alles auswählen
In [13]: 'Staples_north'.startswith('Staple_')
Out[13]: False
Man muss schon auf die Schreibweise achten.

Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 17:45
von akis.kapo
Thanks all,
folgender Code funktioniert tatsächlich:
Code: Alles auswählen
class Horses:
Staples = set()
def __init__(self):
for i in dir(self):
if i.startswith('Staple_'):
self.__class__.Staples.add(i)
def Staple_north(self):
pass
def Staple_south(self):
pass
def clean_staple(self, staple):
pass
Danke @BlackJack für den tipo-tip.

Danke @anogayales für den Hinweis, dass ich das Klassenattribut als Instanzattribut benutzt habe, obwohl ich's eigentlich genau als Klassenattribute vorgesehen habe.
Jetzt kann ich wieder ruhig schlafen.
(Oder auch nicht, in Anbetracht meiner Dummheit & der Hitze draussen)

Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 18:55
von Sirius3
anogayales hat geschrieben:Wenn du in der __init__ Methode wirklich auf das Klassenattribut zugreifen willst, so musst du das auch explizit machen:
Alle Klassenattribute (solange sie nicht von Instanzattributen überlagert werden) sind auch per »self.bla« ansprechbar.
Ein »self.__class__.bla« ist nicht nur nicht schön anzusehen sondern auch unnötig.
@akis.kapo: das Füllen von Klassenattributen in »__init__« ist keine gute Idee. Zum einen ist es zu spät, weil erst ein Exemplar der Klasse erzeugt werden muß, bevor das Attribut gültig ist (eine unglaublich schwer zu findende Fehlerquelle) zum anderen wird dieser Code unnötigerweise bei jedem Erzeugen einer Instanz durchlaufen. Elegant würde man das Problem mit einer Metaklasse lösen oder so:
Code: Alles auswählen
class Horses:
def __init__(self):
pass
def Staples_north(self):
pass
def Staples_south(self):
pass
def clean_staple(self, staple):
pass
Staples = set(name for name in locals() if name.startswith('Staples_'))
print Horses.Staples
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 19:47
von akis.kapo
@Sirius3
locals() und globals() hab ich ja komplett vergessen. Wenn man 'was nicht täglich nutzt in Python ... ist's weg! (*cough* GC?)
Danke für den Hinweis.
Trotzdem ein Einwand zum self.__class__.Staples.add(i)...
Also ich verstehe, dass der Zeitpunkt der Instanzierung einer Klasse hier wohl zu spät ist für die Anforderung und es Fälle gibt, wo Horses.Staples nicht das gewünschte Ergebnis liefert, wenn man es im __init__() setzt, ok - point taken.
Aber angenommen, es muss (for argument's sake) unbedingt ins __init__(), wieso wäre dann self.__class__.Staples.add(i) hässlicher als Horses.Staples.add(i)?
Letzteres ist doch nur fehleranfällig, falls ich aus irgendeinem Grund den Klassennamen ändern will, oder per Copy & Paste ne neue Klasse erstellen will und vergesse, Horses zu ändern.
self.__class__.* referenziert hier per Definition immer die "richtige" (eigene) Klasse, unabhängig vom Namen.
Und von der Schönheit mal abgesehen, wie sonst hätte es gehen können (weil du gemeint hast, es wäre auch unnötig...)?
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 19:55
von BlackJack
@akis.kapo: Das wäre hässlicher weil man die magischen Namen vermeiden sollte wenn es eine Lösung ohne gibt, die nicht deutlich umständlicher ist. Ob `__class__` *immer* richtig ist, wenn man mal an Mehrfachvererbung denkt, ist auch nicht so eindeutig klar. Es gibt als „nicht-magischen” Weg `super()` — was mit seinem eigenen Päckchen von Problemen kommt, weswegen ich das nicht verwende.
Aber in diesem (hypothetischen) Falle ist das ja auch alles unnötig, weil mankein Attribut neu binden möchte, sondern nur ein vorhandenes Klassenattribut über seine Methoden verändern möchte. Und dafür kann man auch auf dem Exemplar einfach `self.attributname` verwenden ohne irgendwelche weiteren Verrenkungen oder Magie.
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 20:09
von akis.kapo
Ja, kann jemand ein Beispiel nennen ohne __class__? Von mir aus mit super() (Probleme hin oder her...)?
Lassen wir mal Mehrfachvererbung aus dem Spiel... sonst wird's zu kompliziert.
Re: TestCases in TestKlasse automatisiert erfassen.
Verfasst: Freitag 2. August 2013, 20:21
von BlackJack
@akis.kapo: Okay, die Wärme macht auch vor mir nicht halt. `super()` war keine Superidee. Man könnte eine Klassenmethode schreiben (`classmethod()`) — die bekommt die Klasse als erstes Argument übergeben. Aber man kann auch einfach den Klassennamen explizit hinschreiben. Wenn man die Klasse umbenenennt, muss man halt ein wenig suchen und ersetzen betreiben. Was man in vielen Fällen sowieso machen muss, denn was Du da veranstaltest (des Arguments wegen) kommt sowieso kaum vor — Zustand gehört nicht auf Klassenebene. Im Normalfall hat man dort Konstanten, und auf die wird oft auch von aussen zugegriffen, und *dann* muss man den Klassennamen explizit wissen.