Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

Acht Wege zur stetigen Qualitätskontrolle – Teil 1

Continous Integration fürs Frontend

Acht Wege zur stetigen Qualitätskontrolle – Teil 1

Die Projekte werden immer komplexer, auch im Frontend. Da reichen ein Vier-Augen-Prinzip oder der extra abgestellte Entwickler nicht mehr aus, um die Qualität des Codes sicherzustellen. Stattdessen bieten sich einige Tools an, mit denen ihr euren Code automatisiert, einfach und zeitsparend kontinuierlich überprüfen könnt.

»Dieser Code ist historisch gewachsen« – welcher Entwickler kennt diesen Ausspruch nicht (nur die ganz, ganz glücklichen unter uns) oder hat ihn nicht schon mal selbst getätigt?! Abgesehen davon, dass sich »historisch« bei dem meisten Code auf wenige Monate und nicht Dekaden bezieht, sind solche Aussprüche meist der fehlenden Qualitätskontrolle geschuldet. Und nein, damit ist nicht der Student am Ende der Kette gemeint, der sich für einen kleinen Nebenverdienst durch unsere Applikation oder Webseite klickt. Geht es um Code-Qualität, ist alleine der Entwickler gefragt. Hier lässt sich keine Schuld auf andere Parteien abwälzen.

Wenn es darum geht, den berüchtigten »Code Smell« in unseren immer komplexer werdenden Projekten beim Kragen zu packen, reicht ein Vier-Augen-Prinzip schon lange nicht mehr aus; Automatisierung ist hier das Stichwort. In Verbindung mit diesen Themen sticht in den vergangenen zwölf Monaten immer häufiger der Begriff des Front-End Ops in die Augen. Dass ihr nicht immer einen extra abgestellten Entwickler benötigt, um eine Qualitätskontrolle zu ermöglichen, zeigen die folgenden Tools und Techniken, die euch zeitsparend und auf einfache Art und Weise helfen, die Qualität eures Codes auf Dauer sicherstellen zu können.

Jenkins und Grunt

Die folgenden Beispiele beziehen sich hauptsächlich auf zwei Tools, die momentan den Standard der Frontend Build-Tools und der Continous Integration Server darstellen. (siehe auch Was ist Continous Integration?). Auch wenn wir uns hier auf Grunt und Jenkins beschränken: Die Konzepte und zugrunde liegenden Techniken sind universal einsetzbar, ob ihr nun auf Gulp anstelle von Grunt oder Travis anstelle von Jenkins vertraut, spielt weniger eine Rolle.

Setup

Solltet ihr noch nicht mit Jenkins oder Grunt vertraut sein, plädiere ich stark dafür, euch in die Themen einzulesen und ein Basic Setup zu installieren, bevor ihr weiter in den Artikel eintaucht, so dass sich die Beispiele Stück für Stück selbst reproduzieren lassen und ihr ein Gefühl dafür bekommst, wie sich die Tools in Verbindung mit eurem eigenen Code darstellen.

Eine gute Einführung in die Installation und die ersten Schritte mit Jenkins findet ihr bei der FH Osnabrück. Wer sich dem Thema Grunt nähern möchte, dem sei der einsteigerfreundliche Beitrag von Christian Siedler »Grunt.js für Frontend Entwickler. Der einfache Einstieg.« empfohlen. Dieser ist zwar leider schon etwas älter, dementsprechend solltet ihr der offiziellen Installationsanleitung GruntJS - Getting started folgen, erklärt aber sehr schön die Grundprinzipien dieses Tools.

Jenkins basiert auf Java und Grunt auf Node.js. Diese Umgebungen sollten auf dem Rechner installiert sein. Eine Beschreibung der Installation würde den Rahmen dieses Artikels sprengen, lässt sich aber sehr schnell mit der Suchmaschine eurer Wahl herausfinden.

Ist Jenkins aufgesetzt, der erste Job konfiguriert und mit dem eingesetzten Versionskontrollsystem verbunden, ist noch ein Schritt nötig, beziehungsweise sehr sinnvoll, nämlich die Installation des Node.js-Plugins für Jenkins. Mit diesem Plugin verwaltet ihr verschiedene Node.js-Versionen pro Job und, in unserem Fall noch wichtiger, installiert Tools wie Grunt oder npm global, so dass ihr sie in jedem Job zur Verfügung habt.

Nun müssen wir noch die Build-Aktion »Execute shell« aktivieren und in das erscheinende Feld Folgendes eintragen:

  1. npm install
  2. grunt build-ci

Sollte noch keine package.json in dem Projekt vorhanden sein, erstellt ihr eine mit dem Kommandozeilen-Befehl npm init; ebenso solltet ihr eine Gruntfile.js-Datei angelegen. Beides wird ausführlich in GruntJS - Getting started behandelt.

In unserem Gruntfile registrieren wir nun noch einen Task, der laufend, mit jedem Abschnitt in diesem Artikel einen neuen Eintrag erhält. Im Prinzip sagen wir Grunt damit, welche Tasks wir gerne in unserer CI-Umgebung ausgeführt hätten.

  1. grunt.registerTask('build-ci', []);

Beachtet bitte: Sofern ihr keinen globalen Grunt-Plugin-Loader wie load-grunt-tasks nutzt, müsst ihr jedes der Plugins via grunt.loadNpmTasks('pluginName') in eurem Gruntfile referenzieren.

Aber nun genug in Vorleistung getreten, ran ans Fleisch, beziehungsweise die Qualitätskontrolle!

JSHint

Als erstes widmen wir uns dem mittlerweile bereits sehr verbreiteten Tool JSHint. Dieses checkt unseren JavaScript-Code nach vordefinierten Regeln und erkennt zum Beispiel, ob wir uns an die vorgegebene Code-Einrückung halten, Variablen benutzen, bevor wir sie initialisiert haben, und noch vieles mehr. Wie ziemlich alle vorgestellten Tools wird JSHint bzw. das dazugehörige Grunt-Plugin grunt-contrib-jshint via npm auf der Kommandozeile des Projektes installiert:

  1. npm install grunt-contrib-jshint --save-dev

Ist dies geschehen, können wir mit folgender Konfiguration das Plugin in unseren Build-Prozess einbinden:

  1. jshint: {
  2.   options: {
  3.     jshintrc: '.jshintrc'
  4.   },
  5.   dev: {
  6.     src: [
  7.       'Gruntfile.js',
  8.       'src/js/**'
  9.     ]
  10.   },
  11.   ci: {
  12.     options: {
  13.       force: true,
  14.       reporter: 'checkstyle',
  15.       reporterOutput: 'report/jshint_checkstyle.xml'
  16.     },
  17.     src: ['<%= jshint.dev.src %>']
  18.   }
  19. }

Dieser Task ist aufgeteilt in zwei Teilbereiche: dev und ci. Damit behalten wir uns die Option vor, mit dem Aufruf grunt jshint:dev eine Ausgabe auf der Kommandozeile zu erhalten (sehr nützlich, wenn wir vor dem Einchecken ins VCS prüfen wollen, ob unser Code in Ordnung ist). Der Aufruf grunt jshint:ci hingegen hat keine direkte Ausgabe auf unserem Schirm zur Folge, im Gegensatz zu dem jshint:dev Task werden hier die Ergebnisse in einer XML-Datei gespeichert, deren Ablageort wir mit der Property reporterOutput angeben können. In unserem Fall wird der Bericht in den Ordner report geschrieben und erhält den Dateinamen jshint_checkstyle.xml. Die Property reporter gibt an, in welchem Format die Ausgabe erfolgen soll. Wir wählen hier checkstyle, da dieses ein für Jenkins verständliches Format ist.

Hinweis: Wir werden noch weitere Dateien in dem Verzeichnis report generieren. Da diese Dateien automatisch erstellt werden, sollten sie nicht ins VCS eingecheckt werden. Bitte adaptiert eure .gitignore o.ä. so, dass dieser Ordner ignoriert wird.

Wer sich über die komische Zeichenfolge innerhalb der src des ci Tasks wundert, dem sei gesagt, dass dies ein Platzhalter ist, der besagt: »Untersuche bitte die gleichen Dateien, die wir weiter oben in der src Property des dev Tasks angegeben haben«; damit stellen wir nur sicher, das beide Tasks den gleichen – unseren – Code untersuchen.

Die letzte Option, die wir noch nicht besprochen haben, ist force: true. Mit dieser Einstellung sagen wir Grunt, dass es nicht abbrechen soll, wenn Fehler auftreten, sondern die anderen Tasks auch bearbeiten soll. Würden wir dies nicht tun, würde das Build jedesmal abbrechen, wenn JSHint einen Fehler findet, und keinen Report erstellen. Damit hätte Jenkins auch später nichts zum Analysieren.

Die unter options angegeben Datei .jshintrc enthält alle Regeln, nach denen unser Code gecheckt werden soll. Schlagt bitte hier in den JSHint Docs nach, welches die passende Konfiguration für euer Projekt ist.

Als letzten Konfigurationsschritt innerhalb unserer Gruntfile tragen wir den jshint:ci Task in unser Sammelbecken für Tasks ein, die als Teil unseres Builds ausgeführt werden sollen:

  1. grunt.registerTask('build-ci', ['jshint:ci']);

Wollen wir nun unsere Ergebnisse von Jenkins analysieren und anschaulich als Graph darstellen lassen, müssen wir noch das Jenkins Plugin Violations installieren und konfigurieren. Ist das Plugin korrekt installiert (eventuell ist ein Neustart des Jenkins-Servers erforderlich), müssen wir es noch innerhalb der Job-Konfiguration aktivieren. Dazu wählen wir aus dem Menü »Add post-built action« den Punkt »Report Violations« aus.

Aktivieren der Einstellungen
Aktivieren der Einstellungen

Nachdem wir das getan haben, sollte sich ein neuer Konfigurations-Eintrag innerhalb der Job-Konfiguration mit dem Titel »Report Violations« befinden. Dort tragen wir unter dem Punkt checkstyle den Pfad zu unserer mit Grunt erstellten XML-Datei ein. Diese befindet sich innerhalb des Arbeitsbereiches von Jenkins und hat normalerweise den gleichen Dateipfad, den wir auch in der Grunt-Konfiguration eingetragen haben, in unserem Fall also report/jshint_checkstyle.xml.

Einstellungen des violations plugins
Einstellungen des violations plugins

Lassen wir nun den Build-Prozess laufen, sollte sich auf unserem Jenkins-Dashboard ein Graph ähnlich diesem hier befinden, der anzeigt, wie viele Fehler JSHint gefunden hat:

Ergebnis des violations plugins
Ergebnis des violations plugins

CSS Lint

Nicht nur für unsere JavaScript-Fans ist Linting im Angebot, auch für alle CSS- und Sass- bzw. SCSS-Jünger gibt es Futter in Form des Plugins grunt-contrib-csslint beziehungsweise grunt-scss-lint.

Diese checken analog zum JSHint Task euer CSS auf diverse Verletzungen der Coding-Styleguides und allgemeiner Fallstricke, in die wir immer wieder treten, wenn wir unsere Styles erstellen. Beide Plugins installieren wir via npm mit:

  1. npm install grunt-contrib-csslint --save-dev

CSS Lint lässt sich wie folgt konfigurieren:

  1. csslint: {
  2.   options: {
  3.     csslintrc: '.csslintrc'
  4.   },
  5.   dev: {
  6.     src: ['src/css/**/*.css']
  7.   },
  8.   ci: {
  9.     options: {
  10.       formatters: [
  11.         {id: 'lint-xml', dest: 'report/csslint_lint.xml'}
  12.       ]
  13.     },
  14.     src: ['<%= csslint.dev.src %>']
  15.   }
  16. }

Auch hier lagern wir die Regelkonfiguration wie beim JSHint Task in eine .csslintrc-Regeldatei aus. Eine Liste der Konfigurationsoptionen für CSS Lint findet ihr hier. Zudem geben wir noch an, dass das Ergebnis unseres CI-Tasks (und wiederum nicht des DEV-Tasks) als XML-Dokument persistiert werden soll, dies geschieht in der Konfiguration des formatters. Zu guter letzt erweitern wir unseren build:ci Task um die Anweisung, unseren CSS-Lint-Task auszuführen:

  1. grunt.registerTask('build-ci', ['jshint:ci', 'csslint:ci']);

Möchten wir das Ergebnis nun im Jenkins erfassen, können wir uns dem bereits aus dem JSHint-Task bekannten Violations Plugins bedienen; dieses bietet uns nämlich die Möglichkeit, mehrere Dateien zu erfassen und dann einheitlich im Graph zu präsentieren. Hierzu fügen wir bei dem Punkt csslint den im formatter, beziehungsweise reporterOutput angegebenen Pfad zu Datei an.

Einstellungen des violations plugins für CSS Lint
Einstellungen des violations plugins für CSS Lint

Und nach dem nächsten Durchlauf erhalten wir einen erweiterten Violations Graph, der nun auch unsere CSS-Dateien versteht:

Einstellungen des violations plugins für CSS Lint
Einstellungen des violations plugins für CSS Lint

Unit Tests

Neben dem Abklopfen unseres JavaScripts auf Regeln und Code-Konventionen besteht der Hauptauftrag eines Continuous-Integration-Systems darin, unseren Code zu testen. Dies kann er natürlich nicht alleine, sondern wir müssen im hier eine Anleitung in Form von Unit Tests mitgeben.

Wer sich bisher noch nicht mit dem Thema auseinander gesetzt hat, dem sei der Artikel Introduction to JavaScript Unit Testing von Jörn Zaefferer empfohlen. Analog zu Jörns Einführung werden wir uns in diesem Kapitel auch mit QUnit als Grundlage für unsere Tests beschäftigen. Auch wenn sich die Konfiguration und die eingesetzten Grunt-Plugins etwas unterscheiden, gilt hier auch: Alles was wir hier mit QUnit abfrühstücken, ist genauso mit anderen Tools wie Jasmine, Mocha etc. möglich.

Sind bereits Unit Tests im Projekt vorhanden, so können wir diese mit Hilfe des Grunt-Plugins grunt-qunit-istanbul ausführen. Dieses Plugin bringt bereits PhantomJS als Umgebung mit, in der wir unsere Tests ausführen können, und bildet auch schon die Grundlage für unser nächstes Kapitel, in dem wir die Code Coverage messen wollen.

  1. npm install grunt-qunit-istanbul --save-dev

Neben diesem Grunt-Plugin müssen wir noch ein weiteres namens grunt-qunit-junit installieren, das auch hier wieder dafür sorgt, dass der CI-Server unsere Ergebnisse auswerten kann.

  1. npm install grunt-qunit-istanbul --save-dev

In unserem Gruntfile legen wir nun zwei neue Tasks an. Zum einen geben wir im qunit_junit Task an, wo die (Jenkins-kompatiblen) Ergebnisse unserer Tests abgelegt werden sollen, und zum anderem sagen wir dem qunit Task, dass wir gerne alle HTML-Dateien des Ordners test ausführen würden. Die Option --web-security=no ist hier optional, da wir aber unsere Tests nicht auf einem Webserver, sondern über das file://-Protokoll ausführen, gehen wir etwaigen »Cross-Origin Ressource«-Problemen aus dem Weg.

  1. qunit_junit: {
  2.   options: {
  3.     dest: 'report/qunit_junit'
  4.   }
  5. },
  6. qunit: {
  7.   options: {
  8.     '--web-security': 'no'
  9.   },
  10.   dev: {
  11.     src: ['test/*.html']
  12.   }
  13. }

Sind die Tasks konfiguriert, können wir sie unserem Auffangbecken für »Codequalität anreichernde Tasks« hinzufügen. Hier ist ganz wichtig zu beachten: Der qunit_junit Task muss vor dem qunit:dev Task ausgeführt werden, sonst wird kein XML-Report für Jenkins erstellt.

  1. grunt.registerTask('build-ci', ['jshint:ci', 'csslint:ci', 'qunit_junit', 'qunit:dev']);

Möchten wir nun die Ergebnisse von Jenkins auswerten lassen, können wir eine weitere »Post-build action« auswählen: »Publish JUnit test result report«.

Aktivieren der Einstellungen
Aktivieren der Einstellungen

Nach der Auswahl erscheint ein neues Konfigurationsfeld, in dem wir wieder den Pfad zu unserer Ergebnisdatei hinterlegen.

Konfiguration
Konfiguration

Nach dem nächsten erfolgreichen Build sollte uns das Jenkins Dashbaord mit einer Übersicht über unsere Tests begrüßen.

Einstellungen des Unit Test Reports
Einstellungen des Unit Test Reports

Code Coverage

Unit Tests automatisch laufen zu lassen, ist die eine Seite der Medaille. Sicher zu gehen, was die Tests eigentlich testen und einen Überblick darüber zu bekommen, was ihr vielleicht vergessen habt, ist die andere.

In solchen Fällen hilft es uns, zu erfahren, welche Codezeilen oder auch Code Branches während unserer Unit Tests ausgeführt werden. Diese Möglichkeit bieten uns Code-Coverage-Tools wie Istanbul. Dank dem im vorausgegangenen Schritt installierten Tool grunt-qunit-istanbul, müssen wir hierfür nichts weiter installieren, sondern nur unsere Grunt-Konfiguration ein wenig abändern.

  1. qunit_junit: {
  2.   options: {
  3.     dest: 'report/qunit_junit'
  4.   }
  5. },
  6. qunit: {
  7.   options: {
  8.     '--web-security': 'no'
  9.   },
  10.   dev: {
  11.     src: ['test/*.html']
  12.   },  
  13.   ci: {
  14.     src: ['<%= qunit.dev.src %>'],
  15.     options: {
  16.       coverage: {
  17.         src: ['src/*.js'],
  18.         instrumentedFiles: 'tmp/instrumented',
  19.         coberturaReport: 'report/cobertura'
  20.       }
  21.     }
  22.   }
  23. },

Vergleicht ihr die hier dargestellte Konfiguration mit der vorherigen, erkennt ihr, dass wir nur den Sub-Task ci hinzugefügt haben. Diesem müssen wir unter dem Punkt coverage dann nur noch in der src Property mitteilen,

  • welche Dateien untersucht werden sollen (hier geht es nicht um unsere Test-Dateien, sondern um unsere wirklichen Webseiten-Skripte),
  • wo – während der Task läuft – ein paar temporäre Dateien abgelegt werden sollen, die nach dem Durchlauf wieder entfernt werden (instrumentedFiles)
  • und wo das Ergebnis unserer harten Arbeit abgelegt werden soll (coberturaReport).

Statt des qunit:dev rufen wir dann noch unseren neuen qunit:ci Task auf und sind von der Grunt-Seite damit bereits fertig.

  1. grunt.registerTask('build-ci', ['jshint:ci', 'csslint:ci', 'qunit_junit', 'qunit:ci']);

Möchten wir nun Jenkins wieder dazu bewegen, uns einen schönen bunten Graphen anzuzeigen, müssen wir das Cobertura Plugin installieren. Ist dies geschehen, können wir als weiteren Punkt unseres »Add post-build action«-Menüs in der Job-Konfiguration den Punkt »Publish Cobertura Coverage Report« auswählen.

Aktivieren der Einstellungen
Aktivieren der Einstellungen

Daraufhin können wir in der Konfigurationsmaske wiederum angeben, wo wir den Cobertura Report von unserem Grunt Task haben ablegen lassen.

Konfiguration
Konfiguration

Das war es auch eigentlich schon. Lassen wir jetzt ein paar Builds durchlaufen, wird Jenkins mit einer detaillierten Analyse auf dem Dashboard aufwarten:

Aktivieren der Einstellungen
Aktivieren der Einstellungen

Soviel zu den ersten vier Tools, morgen gibt es vier nützliche Ergänzungen.

Die Kommentare sind geschlossen.