dedlfix: Falsche Parametertypen

Beitrag lesen

Tach!

Solange alles mein eigener Code ist, würde ich falsche Parametertypen durch Unit-Tests entdecken wollen. Steckst Du in eine Funktion falsche Typen hinein, dürften die Unit-Tests vor die Pumpe fahren.

Ich habe mich gerade ein wenig in Unit-Tests eingelesen und bin dabei unter anderem auf QUnit gestoßen. Aber ich verstehe nicht, wie ich „falsche Parametertypen [damit] entdecken [sollte]“. QUnit ist doch dazu gedacht Codeabschnitte (Funktionen, Units) auf ein erwartetes Ergebnis zu prüfen.

Unit-Tests funktionieren nur dann, wenn man sich auf Ergebnisse für alle möglichen Fälle festgelegt hat. Im Gutfall muss das Ergebnis ebenjenes sein, dass man eigentlich haben will. Für den Fehlerfall (oder auch mehrere unterschiedliche) müssen ebenfalls Reaktionen festgelegt sein, und sei es das Werfen einer Exception. Unit-Tests stellen sicher, dass immer eines dieser erwartbaren Ergebnisse entsteht. Voraussetzung ist, dass sie alle möglichen Fälle abdecken. Natürlich können sie keine Fehler in besonderen Grenzsituationen finden, für die man keinen Test geschieben hat. Sowas passiert auch. Aber man kann dafür sorgen, dass beim Bekanntwerden mit dem Nachtragen eines entsprechenden Tests dieser Fall künftig nicht unentdeckt bleibt.

So interessant ich Unit-Test auch finde (kannte ich bis gerade eben nicht), ich denke nicht, dass sie mich auf meine Frage hin weiterbringen, oder?

Vermutlich nicht. Was innerhalb einer Funktion geschieht, ist dem Unit-Test egal. Der sieht genauso eine Blackbox, wie sie ein Anwender vor sich hat, der nicht in den Code schaut.

Und wie definierst du „mein eigener Code“? »Private Variablen, Funktionen und Sowas«, »Code, den eh kein Anderer zu Gesicht bekommt«, ... ?

Ich nehme an, er meint Code, den man selbst geschrieben und nicht von anderswo bezogen hat. Mit dem zitierten Satz hat er sicher auch von außen geschaut und nicht aus der Sicht des Innenlebens.

Wenn Du eine Bibliothek schreibst, sieht die Sache natürlich anders aus. Da können Prüfungen in der öffentlichen Schnittstelle durchaus sinnvoll sein. Wenn Du mit dem falschen Typ nicht weitermachen kannst, solltest Du auch eine Exception werfen. Es ist ja schließlich ein Programmierfehler, da darf es gerne laut scheppern. Ich denke das ist der Punkt, zu erkennen wann man mit einem falschen Typ nicht weitermachen kann.

Ja, und den kann man nicht pauschal und für alle Fälle gültig beantworten. Es gibt solche und solche und obendrein auch noch solche Situationen.

Angenommen meine Funktion erwartet einen Parameter vom Typ Number und ich prüfe mit typeof und jemand kommt ernsthaft auf die Idee new Number(10) als Parameter zu übergeben. Kann mir das egal sein (--> TypeError werfen) und wirklich nur auf Primitives setzen?

Garbage in, Garbage out. Das ist eine Strategie. Man definiert und dokumentiert, für welche Fälle welches Ergebnis zu erwarten ist und für die anderen erklärt man undefiniertes Verhalten. Das ist sicher nicht die netteste Strategie, denn sie überlässt dem Verwender die Prüfung auf den gültigen Wertebereich. Andererseits müsste der das sowieso machen, um die von dir geworfene Exception zu vermeiden. Aber er hat bei Exceptions auch die Möglichkeit, einfach nur einen Catch-Handler zu implementieren und kann eine Vorfilterung weglassen. Wie schon im vorigen Absatz erwähnt, kann man nicht pauschal sagen, was die beste Lösung ist. Es läuft hinaus auf ein: Kenne die Möglichkeiten und entscheide dich im konkreten Fall für eine bestimmte.

Ich könnte nun versuchen, [...] aber wo endet die Toleranzgrenze?

Die muss man für jeden Fall neu definieren. Da hilft nur Erfahrung, um den Entscheidungsprozess abzukürzen. Auch ein Betrachten aus der Sicht des potentiellen Verwenders kann dabei helfen. Vielen fällt es jedoch schwer, die eigene Gedankenblase zu verlassen und mit anderen Augen auf die Lösung zu schauen.

Aber man sollte schon selber prüfen und ein TypeError werfen, wenn was nicht stimmt, anstatt stur zu versuchen seine Funktion abzuarbeiten und möglicherweise ein automatisches TypeError in der Konsole zu produzieren, oder?

Auf Konsolenausgaben kann man im Prinzip gar nicht reagieren. Eine Exception oder ein definiertes Ergebnis im Fehlerfall kann man hingegen sehr leicht auswerten. Die Konsolenausgabe kann man trotzdem noch einfügen. Die Frage ist dann aber, ob die dann nicht mehr Verwirrung beim Endanwender (so dieser die Konsole beobachtet) verursacht, als sie Nutzen bringt. Vor allem, wenn der verwendende Programmierer auf den Fehlerfall ordnungsgemäß reagiert hat.

Mal nebenbei: Mache ich mir einfach zu viele Gedanken über solche „Kleinigkeiten“ oder darf ich wegen der positiven Bewertung davon ausgehen, dass mein Problem doch gar nicht so doof ist?

Gerade die Beachtung dieser Kleinigkeiten unterscheidet den guten Programmierer von demjenigen, der nur den Geradeausweg für schönes Wetter runtergeschrieben hat.

Da "==" dazu gemacht ist, eine type coercion anzustoßen, und "===" das nicht tut, kann man davon ausgehen, dass "===" eine Spur fixer ist.

Irr-Elefant. Der Unterschied fällt unter die Nichtigkeitengrenze. Die Verständlichkeit des Codes ist in der Regel deutlich höher zu gewichten als solche vermuteten Laufzeitunterschiede[1], besonders bei Sprachen, die per se nicht auf Höchstgeschwindigkeit ausgelegt sind. Es gibt die Verfechter, dass man bei === weniger nachdenken müsse. Arbeitserleichterungen sind ja schön und gut, aber das darf nicht dazu führen, dass man sich damit auf ein trügerisches Ruhekissen bettet.

dedlfix.


  1. Vermutet, weil je nach Engine und deren Optimierer das Ergebnis auch durchaus anders aussehen kann. ↩︎