Über das Thema existiert kein richtiger Thread.
Das Standardmodul ab Python >2.0
http://docs.python.org/lib/module-unittest.html
Für Python 2.0 User:
http://pyunit.sourceforge.net/
Ein schönes Beispiel:
http://www.diveintopython.org/unit_testing/index.html
Wäre schön wenn sich ein paar Erfahrungsberichte sammeln würden zum Umgang mit Unittests.
Unit Tests mit Python
Okay, hier mal ein Beispiel für den Unittest einer Funktion. Der Test ist absichtlich nicht vollständig. Den Code selber kann man sich auch noch optimieren.
Die Funktion nimmt eine Liste und prüft ob die Elemente benachbart sind. Die Nachbarfunktion ist in dem Fall einfach die Distanz x-y == 1. D.h. 2 und 3 sind benachbar, aber nicht 2 und 4 etc.
Die Liste ist dabei ein Ring, das heißt dass der Indexnachbar des letzten Elements das erste Element ist (=wrap around).
Was mir allgemein auffällt ist, dass die Assertions nach dem AssertionError nicht mehr ausgeführt werden. D.h. man sollte den Testcase auch wirklich so organisieren, dass die einzelnen Cases unabhängig sind und zwei Fails in einem Case keinen Informationsgewinn über einem Fail sind.
Die Funktion nimmt eine Liste und prüft ob die Elemente benachbart sind. Die Nachbarfunktion ist in dem Fall einfach die Distanz x-y == 1. D.h. 2 und 3 sind benachbar, aber nicht 2 und 4 etc.
Die Liste ist dabei ein Ring, das heißt dass der Indexnachbar des letzten Elements das erste Element ist (=wrap around).
Code: Alles auswählen
# -*- coding: iso-8859-1 -*-
# std_utils.py
def listGap(li, n):
""" check for (n) gaps in list """
if n > len(li): return False
c = 0 # count number of neighbours
for el in li:
for el2 in li:
i = li.index(el) # index
j = li.index(el2)
next = False
if math.fabs(i-j) == 1:
next = True
# round the corner elements are considered
# [X,.,.,.,.Y] ==> compare X and Y aswell
l = len(li)
if i == l-1 and j == 0 :
next = True
if j == l-1 and i == 0 :
next = True
k = li[i] # values
l = li[j]
if next:
if math.fabs(k-l) == 1:
c+=1 # found one
return c >= n
Code: Alles auswählen
# -*- coding: iso-8859-1 -*-
#unittest_std_utils.py
import unittest
from std_utils import *
class StdUtilTest(unittest.TestCase):
""" Unit test for std_utils """
def testlistGap(self):
""" check List Gap conversion of a list with 2 elements """
assert listGap([0,1],1)
assert listGap([1,0],1)
assert not listGap([0,1],0)
assert not listGap([1,0],0)
assert not listGap([1,1],1)
assert not listGap([2,2],1)
assert listGap([3,3],0)
assert listGap([4,4],0)
def testlistGap(self):
""" check List Gap conversion 5 in the row """
li = [1,2,3,4,5]
assert listGap(li,1)
assert listGap(li,2)
assert listGap(li,3)
assert listGap(li,4)
assert listGap(li,5)
assert not listGap(li,6)
assert not listGap(li,7)
def testlistGap(self):
""" check List Gap conversion : round the corner"""
li = [1,2,3,4,0]
assert listGap(li,1)
assert listGap(li,2)
assert listGap(li,3)
assert listGap(li,4)
assert listGap(li,5)
assert not listGap(li,6)
assert not listGap(li,7)
def testlistGap(self):
""" check List Gap conversion 3 in the row """
li = [1,4,5,6,0]
assert listGap(li,1)
assert listGap(li,2)
assert listGap(li,3)
assert not listGap(li,4)
assert not listGap(li,5)
assert not listGap(li,6)
assert not listGap(li,7)
def testlistGap(self):
""" check List Gap conversion 2 in the row """
li = [1,0,43,44,55]
assert listGap(li,1)
assert not listGap(li,2) # <-- AssertionError!
assert not listGap(li,3)
assert not listGap(li,4)
assert not listGap(li,5)
assert not listGap(li,6)
assert not listGap(li,7)
def testDummy(self):
assert True
assert False
if __name__ == "__main__":
unittest.main()
Was mir allgemein auffällt ist, dass die Assertions nach dem AssertionError nicht mehr ausgeführt werden. D.h. man sollte den Testcase auch wirklich so organisieren, dass die einzelnen Cases unabhängig sind und zwei Fails in einem Case keinen Informationsgewinn über einem Fail sind.
Jain, wie BlackJack schon sagte ist es Zentraler Bestandteil von TDD.gecko hat geschrieben:Unit-Tests != TDD
Aber davon abgesehen dreht sich der von CrackPod verlinkte Thread nicht explizit um TDD, wie man an meinen Ausführungen sehen kann.
TDD im eigentlichen Sinne ist ja erst die Tests zu schreiben und **dann** die Funktionalität zu implementieren die der Test beschreibt Man könnte diese Art der Verwendung auch mit "Spezifikation schreiben" gleichsetzen, wo die Spezifikation für die zu implementierende Funktion/Classe von sich aus eine Validierung vornimmt, wenn man sie startet![1]
Darum geht es aber zum größten Teil nicht in dem Thread. Sondern es geht um die Vorteile von Unit tests und die explizite zunahmen von Code Andeckungstestst (=Coverage) um sich das Leben zu erleichtern(!). Besonders ist der Fokus des Threads auf Coverage gelegt, da sehr wenige sowas nutzen, was sehr schade ist, da es die einzige und "sicherste" Möglichkeit darstellt um eine 100% Abdeckung seines Codes zu erreichen.
OT:
[1]: Die Betrachtungsweise klingt im ersten Augenblick ein wenigeigenartig aber: Was ist letztendlich eine Spezifikation? Eine **ganz genaue** formalisierte Beschreibung eines zum implementierenden Systems. Ob die Spezifikation in natürliche Sprache oder in einem Programmcode (=Untit test) + Kommentaren/Docstrings Ausgedürckt wird, ist letztendlich IMO irrelevant.
Man kann durchaus sich "Utils" und eigene Syntax implementieren, die aus der auf Programmcode basierenden Spezifikation auch eine Komprimierte Form in natürlicher Sprache überführen kann. -- Dafür muss man natürlich die Toolkits ein wenig aufbohren.
----
Wie im anderen Thread erwähnt und auch dem Konsens der meisten Python-Entwicklern entspricht, sollte man das mit Python mitgelieferte ``unittest`` nicht mehr nutzen, weil es mittlerweile viel Bessere und "Pythonischere" (Ja, Leonidas nun habe ich es getan und den Begriff Pythonisch auch benutzt ) Lösungen gibt.gecko hat geschrieben:Über das Thema existiert kein richtiger Thread.
Das Standardmodul ab Python >2.0
http://docs.python.org/lib/module-unittest.html
Für Python 2.0 User:
http://pyunit.sourceforge.net/
Ein schönes Beispiel:
http://www.diveintopython.org/unit_testing/index.html
Wäre schön wenn sich ein paar Erfahrungsberichte sammeln würden zum Umgang mit Unittests.
Gute Lösungen sind py-test und auch nose.
IMO sollte bei dem Thema Unit test auch das Thema Code Abdeckung (=Coverage) nicht fehlen und daher sollte man auch solche hier Aufführen: coverage von Ned Batchelder oder das auf Ned Batchelder basierende coverage figleaf von Titus Brown...
...wo wir auch bei deinem Unit test example angelangt sind. Die zu testende Funktion `listGap` ist schon etwas Komplizierter: Der Unit test liefert mir keine 100% Gewissheit ob wirklich alle Fälle (Die vielen Verzweigungen) abgedeckt werden. Dabei ist für mich gar nicht mal entscheidend das wirklich alle Fälle vom Unit test abgedeckt werden sollen/müssen, nein für mich ist hier das entscheidende (Bei dieser Verzweigungsreichen Funktion) zu wissen, ob da nicht toter Code rumliegt den man rausschmeißen könnte.
Ich geh mal ein wenig weiter und konstruiere mal ein Beispiel:
Angenommen deine Funktion ist vom Unit test nicht 100%ig abgedeckt, aber alle in der Funktion befindlichen Verzweigungen sind momentan erforderlich. Was passiert nun wenn du deine Funktion abänderst (Zum beispiel effizienter machst, etc.) und dadurch die nicht vom Unit test abgedeckten bereiche überflüssig werden (Sprich werden nie aufgerufen)? In dem Fall haben wir es mit unerwünschten Toten Code zu tun, toter Code den du in a**n anderen Projekten betrachten kannst, mangels Coverage und sauberen Unit tests. Klar, wenn du den Totalen Überblick hast, wirst du bei Änderung selber den Überflüssigen Code identifizieren und löschen. Aber welche Gewissheit hast du dafür, das du nicht doch was vergisst? IMO keine, da selbst Profis davor nicht gewappnet sind Daher pädiere ich für eine gewisse "Sicherheit" die nur durch einen Abdeckungstest erreicht werden kann.
Wie gesagt, es geht mir Primär garnicht darum das deine Funktion 100% abgedeckt sein muss (Auch wenn es wünschenswerte ist und IMO nur solcher Code als potenziell "Fehlerfrei" zu betrachten ist.), sondern dass als Minimum ein Abdeckungstest hinter deinem Unit test gekoppelt wird. Es macht das schreiben von Tests halt einfacher und fehlerfreier, als wenn man das alles aus dem Stegreif macht...Und dann kann man immer noch selber entscheiden ob und in wiefern Code 100% abgedeckt werden soll.
Der Vorteil von TDD ist, dass man immer eine Testabdeckung von 100% erreicht. Da nur der Code geschrieben wird, der zum erfolgreichen Ausführen des zuletzt geschriebenen Tests benötigt wird, kommt man nie in die Versuchung, nicht abgedeckten Produktivcode zu schreiben. Beim Schreiben der Tests ergibt sich die Spezifikation des zu testenden Codes nebenbei. Falls der Code dann nicht all das kann, was man sich vielleicht gewünscht hat, so hat man einfach zu wenig Tests geschrieben.
Danke zunächst für das ausführliche Kommentar und die Hinweise zu den Packages.poker hat geschrieben:Jain, wie BlackJack schon sagte ist es Zentraler Bestandteil von TDD.gecko hat geschrieben:Unit-Tests != TDD
Aber davon abgesehen dreht sich der von CrackPod verlinkte Thread nicht explizit um TDD, wie man an meinen Ausführungen sehen kann.
Also die Idee des Unit-Tests ist schon eine andere als das Test-Driven-Development. Man kann sehr gut Unit-Tests machen ohne TDD zu betreiben.
Man kann dazu einfach das Extreme-Programming in die Runde schmeißen und schon ist die Verwirrung perfekt. XP setzt TDD voraus, TDD UT, aber UT setzt weder XP noch TDD voraus.
Da muss ich widersprechen. Die ganze Idee des TDD ist doch, dass der Test die perfekte Dokumentation ist, weil 1. die Anforderungen genau spezifiziert werden müssen und ganz wichtigpoker hat geschrieben: Ob die Spezifikation in natürliche Sprache oder in einem Programmcode (=Untit test) + Kommentaren/Docstrings Ausgedürckt wird, ist letztendlich IMO irrelevant.
2. die Anforderungen automatisch dem Code entgegengestellt werden.
Ich glaube man kann das begrifflich unterscheiden zwischen verschiedenen Test-Modalitäten: Funktionstest, Lasttest, Performancetest, Regressionstest, ...poker hat geschrieben: nein für mich ist hier das entscheidende (Bei dieser Verzweigungsreichen Funktion) zu wissen, ob da nicht toter Code rumliegt den man rausschmeißen könnte.
Das ist in diesem Fall auch genau was passiert ist. Anforderungen ändern sich, als auch das Wissen wie man den Code effizienter umsetzen kann.poker hat geschrieben: Angenommen deine Funktion ist vom Unit test nicht 100%ig abgedeckt, aber alle in der Funktion befindlichen Verzweigungen sind momentan erforderlich.
Was passiert nun wenn du deine Funktion abänderst (Zum beispiel effizienter machst, etc.) und dadurch die nicht vom Unit test abgedeckten bereiche überflüssig werden
(Sprich werden nie aufgerufen)?
Das entscheidende ist jedoch der Arbeitsprozess. Aktuell bin ich relativ ineffektiv was das Inspecten von Code zur Laufzeit angeht. Eine dort zufriedenstellende (kostenlose) IDE habe ich bisher noch nicht gesehen.
Hier der aktuelle Code. Die Funktion liefert jetzt die Liste mit der längsten Verbindung zurück, anstatt nur die Zahl der Verbindungen.
Code: Alles auswählen
#std_utils.py
def listGap(li):
""" return longest linked up list
check also for a link between the last and the first member
(could be extend for full wrap-around)"""
clist = [] # metalist
roli = [] # list of neighboured elements
for i in range(len(li)-1):
j = i+1
neighbour = (li[j]-li[i] == 1)
last = j == len(li)-1
if neighbour:
if li[i] not in roli: roli.append(li[i])
if li[j] not in roli: roli.append(li[j])
#print 'roli', roli
if (neighbour and last) or not neighbour: #
#print 'last'
if len(roli) == 0:
roli.append(li[j]) # append last element
clist.append(roli)
roli = []
#print 'clist: ', clist
maxl = maxList(clist)
return maxl
def listGapHead(li):
meta = []
if li[-1]-li[0] == 1:
wrap = listGap([li[-1]] + li[1:])
meta.append(wrap)
else:
nowrap = listGap(li)
meta.append(nowrap)
return maxList(meta)
def maxList(metalist):
""" return the list with the maximum elements in a list of lists """
rel = []
for l in metalist:
if len(l) > len(rel):
rel = l
return rel
Code: Alles auswählen
#unittest_std_utils.py
# -*- coding: iso-8859-1 -*-
import unittest
from std_utils import *
class StdUtilTest(unittest.TestCase):
""" Unit test for std_utils """
def testlistGap(self):
""" gap list : emtpy list """
a = []
assert listGapHead(a) == a
def testlistGap(self):
""" gap list : one element """
a = [1]
assert listGapHead(a) == a
def testlistGap(self):
""" gap list : two elements """
a = [1,4]
assert len(listGapHead(a)) == 1
def testlistGap(self):
""" gap list : three elements, row 2 """
a = [1,4,0]
assert len(listGapHead(a)) == [1,0]
def testlistGap(self):
""" gap list : three elements, row 3"""
a = [1,2,0]
assert len(listGapHead(a)) == a
def testlistGap(self):
""" gap list : five elements, row 2 """
a = [1,3,4,8,9]
assert len(listGapHead(a)) == 2
def testlistGap(self):
""" gap list : five elements, row 2 """
a = [8,7,6,5,9]
assert len(listGapHead(a)) == 2
def testlistGap(self):
""" gap list : five elements, row 5 """
a = [8,9,10,11,12]
#print 'a: ', listGapHead(a)
assert len(listGapHead(a)) == 5
def testmaxlist1(self):
""" test maximum list function : one element """
assert maxList([[]]) == []
a = []
b = [1]
meta = []
meta.append(a)
meta.append(b)
assert maxList(meta) == b
def testmaxlist2(self):
""" test maximum list function: two elements"""
a = [1]
b = [3,4]
meta = []
meta.append(a)
meta.append(b)
assert maxList(meta) == b
def testmaxlist3(self):
""" test maximum list function: 3 elements"""
a = [1]
b = [3,4]
c = [7,7,7,7]
meta = []
meta.append(a)
meta.append(b)
meta.append(c)
assert maxList(meta) == c
if __name__ == "__main__":
unittest.main()
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Ist aber kein Problem, wer nicht auf die richtige Reihenfolge kommt, der kann es einen Computer berechnen lassen, mit topologischer Sortierung ist das ganz simpelgecko hat geschrieben:Man kann dazu einfach das Extreme-Programming in die Runde schmeißen und schon ist die Verwirrung perfekt. XP setzt TDD voraus, TDD UT, aber UT setzt weder XP noch TDD voraus.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Richtig. Folgende Konvention habe ich mir überlegt:Y0Gi hat geschrieben:Mir misfällt irgendwie, dass du massenweise Methoden gleichen Namens überschreibst.
Code: Alles auswählen
# -*- coding: iso-8859-1 -*-
import unittest
class MyUnit(unittest.TestCase):
""" MyUnitTest description """
def testCase_a_1(self):
""" my test case 1: info about data """
# ////// SETUP TEST DATA /////////
a = 1
# ////// TEST DATA /////////
assert True
self.assertEqual(a,1)
def testCase_a_2(self):
""" my test case 2 similar to 1: a =2"""
# ////// SETUP TEST DATA /////////
a = 2
# ////// TEST DATA /////////
assert True
self.assertEqual(a,2)
def testCase_b_2(self):
""" my test case: b =2 """
# ////// SETUP TEST DATA /////////
b = 2
# ////// TEST DATA /////////
assert True
self.assertEqual(b,2)
if __name__ == "__main__":
unittest.main()