Best Practices für TDD in JavaScript

Preview:

DESCRIPTION

Testgetriebene Entwicklung mit Jasmine und Karma hat sich mittlerweile schon als defacto-Standard etabliert. Routinen ohne Abhängigkeiten lassen sich damit ohne Probleme testen. Die Schwierigkeiten beginnen jedoch schon, wenn es um die Auflösung von Abhängigkeiten geht. In diesem Vortrag werden verschiedene Strategien und Werkzeuge vorgestellt, mit denen Abhängigkeiten zu Objekten und Funktionen oder zum Server abgedeckt werden können. Aber nicht nur Abhängigkeiten stellen Schwierigkeiten bei der testgetriebenen Entwicklung dar, auch der Umgang mit Fixtures ist bei der testgetriebenen Entwicklung mit JavaScript relevant. Abgerundet wird dieser Vortrag mit einigen Best Practices für die testgetriebenen Entwicklung mit JavaScript.

Citation preview

Best Practices für TDD mit JavaScript

Rike / pixelio.de

WHO AM I?

• Sebastian Springer

• aus München

• arbeite bei Mayflower

• https://github.com/sspringer82

• @basti_springer

• Consultant, Trainer, Autor

Warum TDD?

lichtkunst.73 / pixelio.de

Macht sich erst mittel- bis langfristig bezahlt. Initial verursachen Tests mehr Aufwand, als wenn nur Code

geschrieben wird. Bei TDD entstehen die Tests vor dem eigentlichen Code. Geben Sicherheit beim Erweitern und

Umbauen. Tests dokumentieren Quellcode.

Tools & Setup

Stefan Bayer / pixelio.de

Für JavaScript-Testing im Frontend benötigt man ein Test-Framework, das die Formulierung von Tests ermöglicht.

Zusätzlich zum Test-Framework ist eine Infrastruktur erforderlich, die Tests auf verschiedenen Browsern und

Automatisierung ermöglicht.

Test-Frameworks

Häufig eingesetzte Frameworks für JavaScript-Testing:

• Jasmine (http://jasmine.github.io/) • Mocha (http://visionmedia.github.io/mocha/) • qunit (http://qunitjs.com/)

Jasmine

describe(‘Calculator’, function() { beforeEach(function() {…}); afterEach(function() {…});

it(‘should add 1 and 1’, function(done) { … done(); });})

Infrastruktur

Häufig eingesetzte Test-Runner für JavaScript-Testing:

• Karma (http://karma-runner.github.io/) • Testem (https://github.com/airportyh/testem)

Infrastruktur

Tripple A

Wolfgang Dirscherl / pixelio.de

Tripple A

var calc = new Calculator(); var result = calc.add(1, 1); expect(result).toBe(2);

ArrangeAct

Assert

Red, Green, Refactor

Rolf Handke / pixelio.de

Red

Write a failing test.

Die Erwartungshaltung bzw. das zu erreichende Ziel wird als Test formuliert. Der Test schlägt fehl.

Red

describe(‘Calculator’, function() { it(‘should add 1 and 1’, function() { var calc = new Calculator(); var result = calc.add(1,1); expect(result).toBe(2); });});

Red

$ karma run[2014-10-15 13:08:56.773] [DEBUG] config - Loading config /karma.conf.jsChrome 38.0.2125 (Mac OS X 10.9.5) Calculator should add 1 and 1 FAILED ReferenceError: Calculator is not defined at Object.<anonymous> (/spec/calc.spec.js:3:24)Chrome 38.0.2125 (Mac OS X 10.9.5): Executed 1 of 1 (1 FAILED) ERROR (0.021 secs / 0.002 secs)

Green

Make your tests work.

In diesem Schritt unternimmt man alles, um den fehlschlagenden Test grün zu bekommen. Hier sollte der kürzeste und schnellstmögliche Weg gewählt werden.

Einfache Probleme können direkt gelöst werden. Umfangreichere Probleme werden durch einen Fake

(einfaches Return) gelöst.

Green

function Calculator() {}

Calculator.prototype.add = function(a, b) { return a + b;};

Green

$ karma run[2014-10-15 13:15:51.258] [DEBUG] config - Loading config /karma.conf.jsChrome 38.0.2125 (Mac OS X 10.9.5): Executed 1 of 1 SUCCESS (0.019 secs / 0.001 secs)

Refactor

Reduce redundancy.

Duplikate im Code entfernen. Im Idealfall durch Zusammenfassung und Auslagerung. Gilt sowohl für

Produktivcode als auch für Tests. Nicht optimale Implementierungen verbessern.

Immer nur bei grünen Tests durchführen!

Refactordescribe('Calculator', function() { var calc;

beforeEach(function() { calc = new Calculator(); });

it('should add 1 and 1', function() { var result = calc.add(1,1); expect(result).toBe(2); }); it('should subtract 1 from 2', function(){…})});

Refactor

$ karma run[2014-10-15 13:15:51.258] [DEBUG] config - Loading config /karma.conf.jsChrome 38.0.2125 (Mac OS X 10.9.5): Executed 2 of 2 SUCCESS (0.019 secs / 0.001 secs)

TDD und Frameworks?

Rike / pixelio.deRike / pixelio.de

TDD und Frameworks

TDD mit JavaScript funktioniert auch, wenn die Applikation mit einem Framework wie Angular, Backbone oder Ember

erstellt ist. Also keine Ausreden! - ;)

Manche Frameworks (z.B. Angular) unterstützen Entwickler sogar bei der testgetriebenen Entwicklung von

Applikationen.

Automatisierung

Dieter Schütz / pixelio.de

Automatisierung

Automatisierung in der Entwicklungsumgebung. Tests werden automatisch beim Speichern ausgeführt.

Automatisierung in der Continuous Integration. grunt-karma Plugin für Grunt nutzen. Tests werden beim Push ins

Repository ausgeführt.

Kurze Laufzeit

Lothar Henke / pixelio.de

Kurze Laufzeit

Tests, die lange laufen, werden nicht häufig ausgeführt. Werden die Tests nicht ausgeführt, leidet die Qualität.

Three out of four

Timo Klostermeier / pixelio.de

Three out of four

Seid nie mehr als eine Änderung von grünen Tests entfernt!

Auch wenn Umbaumaßnahmen erforderlich sind. Die Applikation sollte immer in möglichst kleinen

überschaubaren Schritten umgebaut werden, ansonsten bringen die Tests nichts. 100 fehlschlagende Tests sind keine

Hilfe, sondern eher ein Hindernis.

Obvious Implementation & Baby Steps

lichtkunst.73 / pixelio.de

Obvious Implementation & Baby Steps

Jeder bestimmt selbst, was offensichtlich ist und nicht getestet werden muss. Auch die Schrittweite bei den Tests

muss jeder selbst festlegen. Grundsätzlich gilt allerdings: Alles, was potenziell kaputt

gehen kann, muss getestet werden. Wird man vom Verhalten der Applikation überrascht, sollte

man auf jeden Fall einen Test schreiben.

Codequalität

Bernd Kasper / pixelio.de

Codequalität

Die Tests existieren länger als der eigentliche Quellcode. Für die Tests sollten die gleichen Qualitätskriterien gelten wie für

den eigentlichen Quellcode. Die Wartbarkeit und Erweiterbarkeit der Tests müssen

erhalten bleiben.

Test Doubles

Regina Kaute / pixelio.de

Spy

Gabi Eder / pixelio.de

Spy

Wrapper um eine Funktion. Aufrufe werden direkt an die ursprüngliche Funktion weitergeleitet. Aufrufe werden

aufgezeichnet. Spy-Objekt kann später abgefragt werden. Spys sollten später zurückgesetzt werden.

Spyit("should create a spy for a method", function () { var myObj = { name: 'Klaus', getName: function () { return this.name; } }; var spy = sinon.spy(myObj, 'getName'); myObj.getName(); expect(spy.calledOnce).toBeTruthy(); myObj.getName.restore();});

Stub

Bernd Kasper / pixelio.de

Stub

Wrapper um eine Funktion wie Spys. Weisen ein definiertes Verhalten auf. Leiten den Aufruf nicht direkt an die

ursprüngliche Funktion weiter. Reduzieren Abhängigkeiten und vereinfachen

Testumgebungen.

Stubit('should return and throw', function () { var myObj = { getName: function () {}, setName: function () {} } var stub1 = sinon.stub(myObj, 'getName').returns('Klaus'); var stub2 = sinon.stub(myObj, ‘setName').throws( new Error(‘BOOH!’) ); expect(stub1()).toBe('Klaus'); });

Mock

Andreas Morlok / pixelio.de

Mock

Ebenfalls Wrapper um eine Funktion. Dienen dazu, die korrekte Verwendung einer Funktion sicherzustellen. Wird die Funktion nicht korrekt verwendet, wird eine Exception

geworfen.

Best Practice: Nicht mehr als ein Mock pro Test.

Mockit ('should work with mocks', function () { var myObj = { name: 'Klaus', getName: function () { return this.name; } } var mock = sinon.mock(myObj); mock.expects('getName').once(); myObj.getName(); myObj.getName(); mock.verify();});

Timers

Tim Reckmann / pixelio.de

Timers

Die Zeit im Browser ist nicht vertrauenswürdig. Fake Timers für eine stabile Testumgebung.

Bieten Kontrolle über Datum und Zeit.

Timers

Server

cre8tive / pixelio.de

Server

Wrapper um XMLHttpRequest bzw. ActiveXObject. Tests werden unabhängig von einer Server-Infrastruktur ausgeführt.

Kontrolle über die Antworten des Fake Servers.

Server

Fixtures

Harald Wanetschka / pixelio.de

Fixtures

HTML-Struktur, die für die Tests benötigt wird. Unabhängigkeit der Tests soll verbessert werden. Durch

vorbereitete Strukturen können Ausschnitte von Workflows getestet werden.

Auslieferung von HTML über den html2js preprocessor von Karma.

jasmine-jquery als Helper für den Umgang mit Fixtures.

Fixtures

beforeEach(function () { $('body').append(window.__html__['fixtures/fx.html']); $('#registerForm').on('submit', validate);});

it ("should show four messages", function () { $('#firstname').val('Klaus'); $('#registerForm').submit(); expect($('.message.visible').length).toBe(4); });

TDD in bestehenden Projekten?:

S. Hofschlaeger / pixelio.de

Nicht alle Tests auf einmal nachziehen!

Stephan Bratek/geralt / pixelio.de

Schnittstellen schaffen

Tim Reckmann / pixelio.de

Boy Scout Rule

Always leave the campground cleaner than you found it. source code

piu700 / pixelio.de

Fragen?

Rainer Sturm / pixelio.de

KONTAKT

Sebastian Springer sebastian.springer@mayflower.de

Mayflower GmbH Mannhardtstr. 6 80538 München Deutschland

@basti_springer

https://github.com/sspringer82

Recommended