Noch mehr Continous Integration fürs Frontend
Acht Wege zur stetigen Qualitätskontrolle – Teil 2
Schnelles Copy-Paste, krude Fixes, offene Tasks oder kaum kommentierter Code. Es gibt viele Stellen, die Korrekturen und Verbesserungen vertragen könnten. Dafür müsst ihr euren Code nicht Zeile für Zeile durchwühlen, denn glücklicherweise gibt es ein paar Tools, die euch dabei unterstützen.
Im ersten Teil ging es um das Setup von Jenkins und Grunt, um dann JSHint, CSS Lint, Unit Tests und Code Coverage einzubinden. Es gibt aber noch weitere Tools, um die Qualität eures Codes zu steigern.
Open Tasks - TODO, FIXME, REVIEW
Wer von uns kennt es nicht: nach Jahren muss Code angepasst werden (oder läuft nicht mehr in einem neuen/aktuellen Browser) und es finden sich so wundervolle Kommentare wie:
- // FIXME: Läuft im IE nur dank dieser 5 jQuery Plugins
- // und viel schwarzer Magie …
- // TODO: Refactor, wirklich, dringend, bald!!!!1einself11!
- // REVIEW: Das muss sich jemand mal anschauen, nur Gott und
- // Bill Gates verstehen wohl warum das läuft (Januar 2003)
Natürlich ist der Kollege, der die Skripte bearbeitete, schon seit Jahren nicht mehr in der Firma, und genauso natürlich hat sich das nie wieder jemand angeschaut, nachdem das Projekt abgegeben wurde. Wäre es nicht schön, wenn wir ein Tool hätten, das uns mit schöner Regelmäßigkeit diese Kommentare um die Ohren wirft? Oder vielleicht noch aggressiver zu Werke geht und uns unsere Projekte vielleicht erst gar nicht bauen lässt, sollten wir zu viele dieser »stilvollen« Kommentare in unseren Code gestreut haben?
Glücklicherweise sind wir Webentwickler nicht die ersten Programmierer auf Erden (aber natürlich die coolsten, bestaussehendsten und sowieso), und so können wir von den Arbeiten anderer profitieren und das Task Scanner Plugin für Jenkins nutzen, das uns exakt die gewünschten Funktionen bietet.
Da wir diesmal keinen Grunt Task benötigen, reicht es, das Plugin im Jenkins zu installieren und für unseren Job zu konfigurieren. Dazu wählen wir aus dem »Add post-build action« Menü unserer Job-Konfiguration den Punkt »Scan workspace for open tasks« aus und bekommen einen neuen Konfigurationseintrag mit selbigem Titel in der Job-Konfiguration präsentiert.
Hier können wir nun beim Input Files to scan
angeben, welche Dateien und Verzeichnisse von dem Plugin analysiert werden sollen. Hier sind Wildcards ebenso erlaubt wie in dem Feld Files to exclude
, in dem wir Dateien und Verzeichnisse von der Analyse ausschließen können. Dies macht besonders Sinn bei 3rd-Party-Skripten wie jQuery, auf die wir sowieso keinen Einfluss haben.
Im Weiteren müssen wir die Tasks tags
konfigurieren und priorisieren, das sind die Schlüsselwörter, auf die der Task anspringen soll. Im täglichen Betrieb haben sich die Keywords: FIXME
mit höchster Priorität, TODO
mit normaler Priorität und REVIEW
mit geringster Priorität bewährt. Wer auf Nummer sicher gehen will, dass alle Tags gefunden werden, der sollte auch noch die Checkbox für Ignore case
aktivieren.
Möchtet ihr nun noch den Job bei einer gewissen Anzahl von Keywords fehlschlagen lassen, könnt ihr mit einem Klick auf den Advanced-Button weitere Einstellungen freischalten. Die von uns benötigte verbirgt sich hinter Status thresholds (Totals)
, hier gebt ihr nun mit einer absoluten Zahl an, ab wie viel gefundenen Keywords das Build unstable ist oder sogar failed.
Das finale Ergebnis stellt sich dann wie folgt auf dem Dashboard dar:
Code Duplication
»Ich könnte den Code jetzt etwas umbauen, das dauert aber noch ne Stunde und ich muss alles wieder testen, oder ich kopiere das einfach schnell…« – CTRL-A, CTRL-C, CTRL-V
. Solche Situationen passieren jeden Tag überall auf der Welt, und wir schauen einfach weg. Gut, dass Computer ganz analytisch denkende Wesen sind, die nicht über solche Missstände hinwegsehen. Zumindest, wenn wir sie richtig instruieren.
Doch vorab – was ist das Problem an dieser Situation? Wenn der Code doch an einer Stelle läuft, warum solltet ihr ihn dann nicht wiederverwenden? Dass ihr ihn wiederverwendet ist dabei nicht das Problem, sondern das »wie«. Duplizieren wir Code durch Copy und Paste, kann es passieren, dass wir bei auftretenden Bugs auf einmal nicht nur eine Stelle fixen müssen, sondern zwei-, drei- oder viermal. Richtig spannend wird das Ganze dann, wenn der Code leicht modifiziert wurde und wir nicht mehr in der Lage sind, die Fixes von einer Stelle einfach ebenso zu kopieren.
Um solche Szenarien von Grund auf zu bekämpfen, können wir uns Tools wie grunt-jscp bedienen. »cp« steht in diesem Falle für »Copy und paste«; das notwendige »d« in »cpd«, das für »detection« steht, wurde vom Autor wohl wegrationalisiert. Solche Tools checken unseren Code auf Ähnlichkeiten und geben uns das Ergebnis entweder auf der Kommandozeile oder als für Jenkins verständliches XML zurück.
Auch dieses Tool lässt sich, wie viele in diesem Artikel, via npm
installieren:
- npm install grunt-jscp --save-dev
Die Konfiguration gestaltet sich auch hier schön einfach. Bei path
geben wir an, welche Pfade untersucht werden sollen, im optionalen output
können wir festlegen, wo der XML-Report für Jenkins gespeichert werden soll. Im Weiteren könnten wir in der Property excludes
noch ein Array angeben, welche Dateien und Verzeichnisse wir nicht untersuchen wollen. Auch hier gilt, 3rd-Party-Code wie jQuery gehört nicht in den Report.
- jscpd: {
- ci: {
- path: 'src/',
- output: 'report/js-cpd.xml'
- }
- }
Ist der Task konfiguriert, können wir das Ergebnis von Jenkins mit Hilfe der Static Code Analysis Plugins auswerten lassen. Sind diese installiert, finden wir in der Job-Konfiguration den Punkt »Publish duplicate code analysis results« in dem »Add post-build action« Menü.
Einmal ausgewählt, bekommen wir einen weiteren Konfigurations-Eintrag, in dem wir den Pfad zu unserer XML-Datei im Arbeitsbereich angeben und festlegen, ab wie viel Meldungen über duplizierten Code uns Jenkins warnen soll.
Nach dem nächsten erfolgreichen Build bekommen wir nun eine Übersicht auf dem Dashboard über alle unsere CTRL-A, CTRL-C, CTRL-V
-Verbrechen mit Hinweisen auf die Code-Stellen, an denen diese auftreten.
Noch ein Wort der Warnung zum Schluss: Leider ist grunt-jscp
kein wirklich intelligentes Tool. Es vergleicht den Code nämlich auf Textebene, was bedeutet: Haben wir eine Variable umbenannt, erkennt grunt-jscp
schon nicht mehr, dass es sich um den gleichen Code handelt wie an anderer Stelle. Klüger ist es, den Code direkt zu untersuchen, beziehungsweise den »Abstract Syntax Tree«, kurz »AST«.
Ein neueres Tool namens jsinspect
, für das auch ein Grunt Task existiert: grunt-jsinspect, macht genau dies. So ist es in der Lage, strukturell ähnlichen Code zu identifizieren, auch wenn die Variablen, Funktionsbezeichnungen etc. nicht gleich sind. Leider hat dieses Tool noch keinen XML-kompatiblen Reporter für Jenkins. Daran wird aber aktiv gearbeitet, und es ist gut möglich, dass bei Erscheinen dieses Artikels ein solcher bereits existiert. Ist dies der Fall, empfehle ich, auf dieses Tool umzusteigen.
- grunt.registerTask('build-ci', ['jshint:ci', 'csslint:ci', 'qunit_junit', 'qunit:ci', 'complexity:ci', 'jscpd:ci']);
Lines of code
Wie viele Zeilen Code haben wir eigentlich für unser Projekt geschrieben? Kann irgendjemand aus dem Stehgreif diese Frage für sich beantworten? Nein, natürlich nicht! Warum sollte er auch? Und ja, diese Frage ist berechtigt. Zwar ist es ganz interessant zu sehen, wie ein Projekt mit der Zeit wächst, noch etwas interessanter vielleicht sogar wie das Verhältnis von CSS und JavaScript aussieht – aber einen praktischen Nutzen daraus ziehen wir selten.
Die Ratio Code-Zeilen zu Code-Dokumentation ist jedoch durchaus von Interesse. Damit sind natürlich nicht irgendwelche externen Dokumente in obskuren Projekt-Wikis gemeint, sondern Dokumentationen in unserem Code, also alles, was sich zwischen den Zeichen /* */
, <!-- -->
und //
befindet.
Warum ist dies so interessant? Die Ratio Kommentar zu Code zeigt uns einiges über die Qualität des geschriebenen Codes aus. Denn wem nützt schon das cleverste JavaScript, geschrieben vom intelligentesten Programmierer der Welt, wenn es von einem anderen Programmierer nicht verstanden wird? Deswegen: Kommentare sind wichtig, und ein Satz von 33% Prozent Code-Kommentaren (wenn wir davon ausgehen, dass der geschriebene Code 100% ausmacht) in qualitativ hochwertigem Code nicht selten.
Hier kommen Tools wie grunt-sloccount ins Spiel, die uns genau diese Daten geben, die wir wiederum von Jenkins in einem Graph verständlich aufbereiten lassen können.
Auch dieses Tool installieren wir via npm
:
- npm install grunt-sloccount --save-dev
Die Konfiguration des Grunt-Tasks ist ziemlich schnell erledigt. Innerhalb des options
Properties müssen wir dem Task sagen, wo die generierte Datei mit den Ergebnissen abgelegt werden soll; dies erledigt die reportPath
property. Der src
Property können wir nun ein Array mit Pfaden und Platzhaltern übergeben, in dem wir dem Task sagen, welche Dateien untersucht werden sollen:
- sloccount: {
- options: {
- reportPath: 'report/sloc.sc'
- },
- src: ['src/*.js', '*.html', 'src/*.css']
- }
Ab mit dem Task in unser CI Sammelbecken:
- .registerTask('build-ci', ['jshint:ci', 'csslint:ci', 'qunit_junit', 'qunit:ci', 'complexity:ci', 'jscpd:ci', 'sloccount']);
Um das Ergebnis nun innerhalb unseres Jenkins-Dashboardes anzeigen zu können, müssen wir im Jenkins das SLOCCount Plugin installieren und in der Job-Konfiguration »Publish SLOCCount analysis results« als »Post-build action« auswählen.
In unserem neuen dann erscheinenden Konfigurationsfeld »Publish SLOCCount analysis results« müssen wir noch den Pfad zu der generierten Datei mit den Ergebnissen der Analyse angeben. Im Regelfall ist dies der gleiche Pfad wie in der reportPath
Property in unserem Gruntfile.
Nach dem nächsten Build sollte uns Jenkins mit zwei Graphen auf unserem Job-Dashboard begrüßen, welche die Ergebnisse darstellen.
Dependency Graph
Nicht nur XML, JSON oder ähnlich formatierte Daten, auch HTML-basierte Dokumente können von Jenkins angezeigt werden. Insbesondere interessant ist dies für generierte Dokumentationen jeglicher Art. Eines dieser Beispiele für generierte Dokumentationen sind Abhängigkeitsgraphen, die anzeigen, in welcher Beziehung Module und Dateien zueinander stehen – quasi ein Stammbaum für unser Projekt.
Grade im JavaScript-Bereich, in dem Modularisierung das Buzzword der letzten Jahre darstellt, ist es sehr interessant, die Abhängigkeiten von Komponenten zu überwachen, besonders wenn es ans Refactoring geht. Welche Module könntet ihr »entfernen«, weil sie besonders groß sind und doch nur von einer Datei benötigt werden (vielleicht nicht mal im vollen Funktionsumfang), oder wo ist es sinnvoll, einzelne Code-Fragmente zu größeren Gruppen zusammenzufassen? All diese Fragen kann uns ein Abhängigkeitsgraph, optisch hübsch aufbereitet, beantworten.
In unserem Fall benutzen wir ein spezielles Tool grunt-dependencygraph von Kenneth Auchenberg, das sich unter anderem an RequireJS andocken kann, um an die benötigten Informationen heranzukommen. Die Installation ist recht einfach:
- npm install grunt-dependencygraph --save-dev
Nun haben wir den Task in unserem Taskrunner zur Verfügung und müssen nur noch angeben, welche JavaScript-Dateien untersucht werden sollen (Property targetPath
), in welchem Format sie vorliegen (Property format
) und wo das in HTML gehaltene Ergebnis des Graphen abgelegt werden soll (Property outputPath
):
- dependencygraph: {
- targetPath: 'src/js/',
- outputPath: 'report/dependencies',
- format: 'amd'
- },
Schnell noch den Task zu unserem CI-Runner hinzufügen:
- .registerTask('build-ci', ['jshint:ci', 'csslint:ci', 'qunit_junit', 'qunit:ci', 'complexity:ci', 'jscpd:ci', 'sloccount', 'dependencygraph']);
Innerhalb von Jenkins müssen wir nun das HTML-Publisher-Plugin installieren und angeben, wo er das HTML Dokument im Arbeitsbereich finden kann. Das ist im Prinzip der gleiche Pfad, den wir oben in der outputPath
property im Grunt-Task angegeben haben.
Dazu wählen wir in unserer Job-Konfiguration das Plugin als »post-build action« Publish HTML reports
aus, und dann sollte in unserer Konfigurations-Oberfläche eine Eingabemaske erscheinen, die wie folgt ausschaut:
Wichtig hier: Die Checkbox bei »Allow missing report« sollte ausgewählt werden, nur für den Fall, dass unser Grunt-Task aus unerfindlichen Gründen mal fehlschlagen sollte (was durchaus mal vorkommen kann), damit das Build trotzdem als erfolgreich gewertet wird.
Nach dem nächsten erfolgreich Build sollten wir im Dashboard einen Link auf unser erstelltes HTML Dokument erhalten.
Das HTML-Publisher-Plugin kann für alle möglichen (zum Beispiel auch für bereits existierende) HTML-Dokumente genutzt werden. Darüber können dann auch Dokumentationen, Styleguides oder sogar im Browser laufende Unit Tests dem Job hinzugefügt werden, eine super Sammelstelle für alle projektbegleitenden Entwickler-Dokumente.
Fazit
Der Kampf gegen »historisch gewachsenen« Code ist kein leichter, und ganz sicher sind diese handvoll Tools auch nicht die »magischen Patronen« im Revolver. Aber sie zeigen uns, dass wir mit ein paar schnellen Handgriffen die Wartbarkeit unseres Codes viel effizienter kontrollieren können als bisher. Dementsprechend: Nehmt die CI-Server den Backend-Menschen aus der Hand und stellt etwas Sinnvolles damit an! Wie es geht, wisst ihr ja jetzt.
Kommentare
Manuel
am 16.12.2014 - 12:13
Es gibt eigentlich kaum ein CI-Tool das Jenkins annähernd nahe kommt. Ich habe mir Gitlab und Gitlab-CI ("hübsch") angesehen, bezweifle aber, dass sich das ebenso gut erweitern lässt wie Jenkins. Sehe ich das richtig, dass Jenkins sätmliche Tests mir grafisch im Backend darstellt? Weil um das geht es (mir) letzten Endes auch, dass solche Ergebnisse nicht nur Entwickler selber zu sehen bekommen sondern auch andere Beteiligte leicht aufrufen können. Am besten man lässt Jenkins einfach in einem Docker-Container laufen, dann erspart man sich das ganze Prozedere der Installation.
Frontend Developer + Dev Op = Frontend Op. Dann darf man sich nun mittlerweile auch offiziell so schimpfen!
Sebastian Golasch (Autor)
am 17.12.2014 - 17:43
Lars
am 16.12.2014 - 20:32
Wer bisher kein Frontend-Testing durchfürhrt sollte mal einen Blick auf dalekjs.com werfen. Nachdem man innerhalb von Minuten seine ersten Tests (z.B.
.assert.text('#bar .bar', 'text', 'this is a description')
) geschrieben hat, kann man diese anschließend auch direkt via grunt auf dem CI (Jenkins) ausführen lassen. Einfach "junit" (reporter: ['html', 'junit']
) mit in das Gruntfile aufnehmen und das xml-file (**/report/dalek.xml
) in der Jenkins-Job-Config eintragen, fertig! :)Mfg Lars
Sebastian Golasch (Autor)
am 17.12.2014 - 17:45
Matthias Mees (Webkraut)
am 17.12.2014 - 17:48
Wat? Wieso nicht? ;-)
Sebastian Golasch (Autor)
am 17.12.2014 - 17:57
Stefanie
am 03.02.2015 - 09:25
Ich teile die Meinung nicht, eine hohe Kommentar-Durchsetzung wäre ein Zeichen für einen qualitativ hochwertigen Code. Eher ist das Gegenteil der Fall. Code sollte möglichst selbsterklärend und leicht verständlich sein und Kommentare nur dort eingesetzt werden, wo sie wirklich sinnvoll sind.
Schön beschrieben z.B. hier: http://www.sebastianviereck.de/clean-code-richtige-und-falsche-kommentare/
Die Kommentare sind geschlossen.