JavaScript-Fehler beheben: Den Bugs auf der Spur

(Grafik: Flaticon / Freepic)
Am Ende einer langen Suche nach einem Fehler, der im Internet Explorer aufgetreten ist, stellt sich ein fehlendes oder falsch platziertes Semikolon als der Übeltäter heraus: Das ist Entwickleralltag. Dabei sollten sich Entwickler heute nicht länger mit Syntaxfehlern herumschlagen, weil statische Code-Analysen über JSHint, JSCS oder ESLint entsprechende Hilfe leisten. Neben einer automatischen Fehler- und potenziellen Problemsuche können diese auch die im Team oder Unternehmen festgelegten JavaScript-Konventionen überprüfen.
Die Demo zum Artikel
Ergänzend zum Artikel gibt es ein Github Repository, indem eine Vielzahl der beschriebenen Grunt Tasks bereits eingerichtet sind [1]. In dieser Demo führen folgende Befehle statische Code-Analysen im Terminal aus:
Statische Code-Analysen
grunt eslint grunt jscs grunt jshint
Listing 1
Wer bereits JSCS im Einsatz hat, sollte perspektivisch zu ESLint wechseln, da sich beide Teams dazu entschlossen haben, künftig zusammenzuarbeiten und die Entwicklung von JSCS zugunsten von ESLint einzustellen.
Debugging im Browser
Die gängigen Browser haben mächtige Werkzeuge, um JavaScript effektiv zu debuggen. Die Developer-Tools von Chrome und Firefox liefern sich dabei seit Jahren ein Kopf-an-Kopf-Rennen. Einen klaren Gewinner gibt es nicht und so entscheidet am Ende die persönliche Präferenz. Ein grundlegendes Werkzeug ist beim Debugging im Browser noch immer die JavaScript-Konsole. Hier loggt der Browser die zuvor im Quellcode definierten Ausgaben.
Debugging in der Browser Konsole
console.log('test');
Listing 2
Die gängigen Browser orientieren sich an der „Console API“, die unter anderem umfangreiches Profiling, die Ausgabe des aktuellen Stack-Trace und sogar individuelle Zeitmessungen erlaubt.
Funktionen der „Console API“
console.profile('test'); console.profileEnd(); console.trace(); console.time('test'); console.timeEnd('test');
Listing 3
Wirklich spannend wird es jedoch erst mit der „Command Line API“, die das Monitoring von Funktionen und Events erleichtert.
Funktionen und Events monitoren
monitor(function); monitorEvents(document, 'click'); getEventListeners(document);
Listing 4
Die JavaScript-Konsole erlaubt auch die Eingabe und somit das Ausführen von Quellcode im Browser – also ohne den Umweg über die IDE.
Breakpoints
Vielen Entwicklern erscheint es zunächst reizvoll, ihren Quellcode mit unzähligen Konsolen-Ausgaben zu bestücken, mit denen sie Variablen, Events oder Conditions nachvollziehen können. Doch was wäre, wenn sie die Ausführung des Quellcodes an einem bestimmten Zeitpunkt pausieren lassen und von da an schrittweise untersuchen könnten? Wie wäre es mit der Manipulation von Variablen und Parametern? So genannte „Breakpoints“ machen dies möglich. Durch die Platzierung eines „debugger statement“ legt der Browser an entsprechender Stelle eine Pause ein und liefert nebenbei den für das Debuggen nützlichen Call-Stack.
Mittels debugger statement die Ausführung anhalten
debugger;
Listing 5
Um ein Vielfaches mächtiger ist die Steuerung innerhalb der Developer-Tools. Neben den statischen Breakpoints, die an eine bestimmte Code-Zeile gebunden sind, gibt es hier auch solche, die folgende Ereignisse im Browser auslösen:
- Veränderung eines DOM-Knoten
- Auslösen eines XHR
- Auslösen eines Event Listener
- Werfen einer Uncaught Exception
Auf diese Weise können Entwickler JavaScript von Drittanbietern ohne Vorkenntnisse untersuchen und anhand von ausgelösten Ereignissen zum Quellcode springen.
Sourcemaps
Wer komprimierten Quellcode vernünftig debuggen will, muss Sourcemaps generieren. Diese stellen eine Verknüpfung zum ursprünglichen Quellcode her und zeigen beispielsweise das zu JavaScript kompilierte CoffeeScript direkt im Browser an.
Debugging externer Geräte
Zu den großen Herausforderungen in Zeiten des Responsive Designs gehören vor allem externe Geräte. Zum Beispiel gilt die mobile Browser-Version von Safari nicht grundlos als neuer Internet Explorer: Apple hinkt seit Jahren in der Entwicklung hinterher und bemüht sich nicht gerade, die Bedürfnisse der Community zu erfüllen. Entwickler, die eine mangelhaft implementierte JavaScript API oder nicht funktionierende Touch-Gesten auf einem externen Gerät debuggen wollen, können sich mit Hilfe von mindestens drei Ansätzen auf die Fehlersuche begeben: dem USB-, dem WLAN- und dem Browser-Remote-Debugging.

Chrome gehört zu den Vorreitern im Bereich USB-Remote-Debugging. Über eine spezielle URL können Entwickler sich die mit dem PC verbundenen Android-Geräte anzeigen lassen. (Screenshot: Google)
USB-Remote-Debugging
Der Klassiker hierbei ist das USB-Remote-Debugging, bei dem ein USB-Kabel den Test-PC mit dem externen Gerät verbindet. In den Entwickleroptionen von Android befindet sich ein Schalter, um es zu aktivieren. Chrome, Firefox und Safari bieten dafür unterschiedliche Optionen:
- Chrome: Der Browser gehörte zu den Vorreitern dieser Methode. Über die URL chrome://inspect können sich Entwickler die mit dem PC verbundenen Android-Geräte über den Browser anzeigen lassen und gegebenenfalls konfigurieren. Dabei ist es unerheblich, um welche Art von Gerät es sich dabei handelt, solange der mobile Browser Chrome oder eine App mit Hilfe der WebView offen sind.
- Firefox: Einige Zeit später kam die WebIDE für Firefox heraus, die ähnliche Möglichkeiten für die mobile Version von Firefox anbietet. Wie bei den älteren Chrome-Versionen müssen Entwickler die Erlaubnis für das USB-Debugging zunächst jedoch in den erweiterten Einstellungen des mobilen Firefox aktivieren. Dann zeigt der Browser Geräte, die verfügbar sind, in der WebIDE an. Die Developer-Version des Firefox enthält außerdem das experimentelle Add-on „Valence“ (Firefox Tools Adapter), mit dem sich auch
Nicht-Gecko-Browser ansprechen lassen. Die Autoren der Dokumentation
berichten von erfolgreichen Tests gegen die mobile Browser-Version von
Chrome und Safari sowie gegen Apps, die auf iOS mit einer WebView laufen. - Safari: Die mobile Browser-Version bringt ebenfalls das
Handwerkszeug zum USB-Debugging mit. Nachdem man die Developer-Tools auf
dem Desktop und den Web-Inspector auf dem mobilen Safari aktiviert hat,
erscheint im Menü „Entwickler“ das externe Gerät. Selbstverständlich braucht man dazu einen
Apple-Computer.

In Firefox läuft das USB-Remote-Debbuging über die WebIDE, die ähnliche Funktionen wie Chrome bietet. (Screenshot: Google)
WLAN-Remote-Debugging
Zuerst die schlechte Nachricht: Debugging über das lokale Netzwerk
ist mit Chrome nur im Terminal zu bewerkstelligen. Vermutlich verliert
die offizielle Dokumentation deshalb auch kein Wort darüber. Es gibt
also keine offizielle Anleitung, die unter anderem Root-Zugriff
benötigt.
Die gute Nachricht: Firefox macht es besser, denn in der WebIDE ist Debugging über das lokale Netzwerk schon seit einigen Releases fest verbaut und kinderleicht. Hierfür müssen Entwickler in den erweiterten Einstellungen des mobilen Firefox nur die Erlaubnis für WLAN-Debugging aktivieren und schon erscheint das Gerät in der WebIDE. Um die Verbindung mit dem externen Gerät zu bestätigen, muss man eine Client-Identifikation über einen QR-Code-Scanner bestätigen.
Browser-Remote-Debugging
Wenn kein direkter Zugang über USB oder WLAN möglich ist oder es keinen Support für das externe Gerät gibt, sitzen Entwickler zuweilen ziemlich in der Patsche. Glücklicherweise gibt es auch in diesem Fall eine Lösung. Während die Browser-Hersteller über „Remote Debugging Protocols“ arbeiten, machen sich Werkzeuge wie Weinre, JS Console und Vorlon.JS die so genannte „Dynamic Script Injection“ zunutze.

Browser-Remote-Debugging lässt sich zum Beispiel mit dem Werkzeug Vorlon.JS umsetzen. Dieses hat im Vergleich zu den Developer-Tools allerdings nur einen sehr begrenzten Funktionsumfang. (Screenshot: Vorlon)
Im folgenden Beispiel kommt Vorlon.JS von Microsoft zum Einsatz. In der Demo startet ein Vorlon.JS-Server im Terminal mit dem folgendem Befehl, der sich anschließend unter http://localhost:1337 über einen Browser aufrufen lässt:
Vorlon.JS-Server starten
grunt vorlon
Listing 6
Nun muss man nur noch die „vorlon.html“ aufrufen und schon ist im Vorlon.JS-Dashboard ein neuer Client registriert. Die Kommunikation zwischen dem Server und dem Client verläuft über das Skript http://localhost:1337/vorlon.js in der „vorlon.html“. Wirklich beeindruckend ist der Proxy, der sich über http://localhost:1337/httpproxy erreichen lässt. Denn hier können Entwickler das DOM einer beliebigen Website in Vorlon.JS laden und untersuchen.
Das Werkzeug ist plattformunabhängig, aber Entwickler können damit nicht auf die internen Schnittstellen der Browser zugreifen. Vorlon.JS ist deshalb keine Allzweckwaffe oder gar ein Ersatz für native Developer-Tools. Im Vergleich zu den nativen Developer-Tools ist der Funktionsumfang
überschaubar, auch wenn die Vorlon-Macher mit Plugins versuchen, diesen
Limitierungen entgegenzuwirken.
Unit Testing
Das automatisierte Testen kritischer Quellcode-Bereiche ist ein wesentlicher Punkt der Qualitätssicherung und Fehlerprävention. Entwickler sollten sich dabei weniger im Debugging des kryptischen Quellcodes üben, als viel mehr ihre Zeit und Energie in die testgetriebene Refaktorisierung (TDR) investieren. In der JavaScript-Welt gibt es dafür unzählige Testing-Frameworks und Libraries. Doch nur wenige überzeugen im Alltag. Die Erfahrung zeigt, dass sich für eine erfolgreiche Testing-Suite folgende Komponenten bewährt haben:
- Mocha – das eigentliche Test-Framework
- Chai – eine Assertion-Library
- Sinon – eine Test-Doubles-Library
In der Demo startet der Aufruf der Datei „mocha.html“ die Testing-Suite direkt in einem Browser:

So mancher Entwickler hat seinen ganz eigenen Humor. Für das Unit-Testing-Tool Mocha gibt es zum Beispiel einen witzigen Reporter, der an die Regenbogen-Katze „Nyan“ angelehnt ist. (Screenshot: Mocha)
In der Demo lässt sich die Testing-Suite im Terminal mit folgendem Befehl ausführen:
Testing-Suite mocha ausführen
grunt mocha
Listing 7
In seltenen Fällen müssen JavaScript-Entwickler einen Test, der Schwierigkeiten macht, direkt im Browser debuggen. Dafür nutzt die Demo dieses Artikels den Node-Inspector, der im Terminal mit den folgenden Befehlen startet. Anschließend lässt er sich unter http://localhost:8080/debug?port=5858 via Chrome aufrufen.
Node-Inspector starten
grunt node-inspector grunt mocha-debug
Listing 8
Cross-Browser-Testing
Die hohe Schule des Unit-Testing ist mit Hilfe so genannter Test-Runner möglich, die eine weitere Abstraktionsebene über dem Testing-Framework bilden. Test-Runner erstellen einen Webserver, der die Testing-Suite auf lokalen oder Cloud-basierten Browsern ausführen kann und anschließend die Testergebnisse einsammelt. Da nicht jeder Entwickler über ein Device-Lab verfügt, gibt es Cloud-Testing-Anbieter wie Browserstack und SaurceLabs, um den Code auch mal auf einem älteren Android oder dem aktuellen iPhone auf Herz und Nieren prüfen zu können.
Der hauseigene Testing-Runner des AngularJS-Teams unterstützt PhantomJS sowie lokale Browser und bietet offizielle Plugins für SaurceLabs und Browserstack.
Karma starten
grunt karma
Listing 9
Das Projekt stammt aus der YUI-Familie, deren Entwicklung mittlerweile eingestellt ist. Es unterstützt PhantomJS sowie lokale Browser, SaurceLabs und Browserstack über eine hauseigene Tunnel-Software.
Yeti starten
grunt yeti
Listing 10
Das von dem Entwickler Roman Shtylman ins Leben gerufene Projekt unterstützt PhantomJS sowie lokale Browser und hat eine fest verbaute SaurceLabs-Intergration.
Zuul starten
grunt zuul
Listing 11

Der Testing-Runner des AngularJS-Teams unterstützt PhantomJS sowie lokale Browser und bietet offizielle Plugins für SaurceLabs und Browserstack. Ein Videocast auf der Startseite führt in die Arbeit mit Karma ein. (Screenshot: Karma)
Fazit
Der Entwickler Remy Sharp bezeichnet Debugging in seinen Talks nicht umsonst als eine Kunstform, die sich im Wesentlichen um das Replizieren, Isolieren und Eliminieren von Fehlern dreht. Grundsätzlich hat er damit recht. Allerdings ist zu häufiges Debugging vor allem ein Anzeichen für eine schlechte Software-Architektur oder die mangelnde Disziplin bei der Entwicklung. Im Vergleich zur klassischen Entwicklung – mit entsprechendem Debugging – funktioniert die testgetriebene Entwicklung (TDD) wesentlich besser und erzeugt darüber hinaus qualitativ sehr hochwertigen und bereits getesteten Quellcode.
Im Alltag geht es jedoch meist nicht ohne Debugging. Deshalb ist es wichtig, die vorhandenen Möglichkeiten zu kennen, um Fehler zielgerichtet und effektiv beheben zu können. Es braucht jedoch auch ausreichend Disziplin, damit Entwickler nicht in alte Muster zurückfallen.
Langer Artikel und trotzdem nicht schön zu lesen. Alle Themen werden nur angerissen, meist nur mit ein oder zwei Sätze. Leider fehlen weiterführende Links zu den einzelnen Themen.
Spannendes Thema und trotzdem habe ich nichts gelernt. Sehr Schade.
Danke für das Feedback.
Im Wesentlichen hast du es auf den Punkt gebracht und ich kann deine Kritik absolut nachvollziehen. Mir ist die Quadratur des Kreises offenbar nicht gelungen, denn ich wollte auf keine Themen verzichten, obwohl mir im Print-Magazin nur drei Seiten zur Verfügung standen. Am Ende wurde auch noch sehr viel gekürzt und von der Redaktion umgeschrieben :-)
Um das zu kompensieren, hatte ich mir die Mühe gemacht dem Leser ein Demo auf GitHub an die Hand zu geben, wo man Schritt für Schritt nachvollziehen kann.
https://github.com/redaxmedia/javascript-testing-stack
Die weiterführenden Links werde ich an die Redaktion übermitteln.