59
Best Practices für TDD mit JavaScript Rike / pixelio.de

Best Practices für TDD in JavaScript

Embed Size (px)

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

Page 1: Best Practices für TDD in JavaScript

Best Practices für TDD mit JavaScript

Rike / pixelio.de

Page 2: Best Practices für TDD in JavaScript

WHO AM I?

• Sebastian Springer

• aus München

• arbeite bei Mayflower

• https://github.com/sspringer82

• @basti_springer

• Consultant, Trainer, Autor

Page 3: Best Practices für TDD in JavaScript

Warum TDD?

lichtkunst.73 / pixelio.de

Page 4: Best Practices für TDD in JavaScript

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.

Page 5: Best Practices für TDD in JavaScript

Tools & Setup

Stefan Bayer / pixelio.de

Page 6: Best Practices für TDD in JavaScript

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.

Page 7: Best Practices für TDD in JavaScript

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/)

Page 8: Best Practices für TDD in JavaScript

Jasmine

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

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

Page 9: Best Practices für TDD in JavaScript

Infrastruktur

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

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

Page 10: Best Practices für TDD in JavaScript

Infrastruktur

Page 11: Best Practices für TDD in JavaScript

Tripple A

Wolfgang Dirscherl / pixelio.de

Page 12: Best Practices für TDD in JavaScript

Tripple A

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

ArrangeAct

Assert

Page 13: Best Practices für TDD in JavaScript

Red, Green, Refactor

Rolf Handke / pixelio.de

Page 14: Best Practices für TDD in JavaScript

Red

Write a failing test.

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

Page 15: Best Practices für TDD in JavaScript

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); });});

Page 16: Best Practices für TDD in JavaScript

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)

Page 17: Best Practices für TDD in JavaScript

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.

Page 18: Best Practices für TDD in JavaScript

Green

function Calculator() {}

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

Page 19: Best Practices für TDD in JavaScript

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)

Page 20: Best Practices für TDD in JavaScript

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!

Page 21: Best Practices für TDD in JavaScript

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(){…})});

Page 22: Best Practices für TDD in JavaScript

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)

Page 23: Best Practices für TDD in JavaScript

TDD und Frameworks?

Rike / pixelio.deRike / pixelio.de

Page 24: Best Practices für TDD in JavaScript

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.

Page 25: Best Practices für TDD in JavaScript

Automatisierung

Dieter Schütz / pixelio.de

Page 26: Best Practices für TDD in JavaScript

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.

Page 27: Best Practices für TDD in JavaScript

Kurze Laufzeit

Lothar Henke / pixelio.de

Page 28: Best Practices für TDD in JavaScript

Kurze Laufzeit

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

Page 29: Best Practices für TDD in JavaScript

Three out of four

Timo Klostermeier / pixelio.de

Page 30: Best Practices für TDD in JavaScript

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.

Page 31: Best Practices für TDD in JavaScript

Obvious Implementation & Baby Steps

lichtkunst.73 / pixelio.de

Page 32: Best Practices für TDD in JavaScript

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.

Page 33: Best Practices für TDD in JavaScript

Codequalität

Bernd Kasper / pixelio.de

Page 34: Best Practices für TDD in JavaScript

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.

Page 35: Best Practices für TDD in JavaScript

Test Doubles

Regina Kaute / pixelio.de

Page 36: Best Practices für TDD in JavaScript

Spy

Gabi Eder / pixelio.de

Page 37: Best Practices für TDD in JavaScript

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.

Page 38: Best Practices für TDD in JavaScript

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();});

Page 39: Best Practices für TDD in JavaScript

Stub

Bernd Kasper / pixelio.de

Page 40: Best Practices für TDD in JavaScript

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.

Page 41: Best Practices für TDD in JavaScript

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'); });

Page 42: Best Practices für TDD in JavaScript

Mock

Andreas Morlok / pixelio.de

Page 43: Best Practices für TDD in JavaScript

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.

Page 44: Best Practices für TDD in JavaScript

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();});

Page 45: Best Practices für TDD in JavaScript

Timers

Tim Reckmann / pixelio.de

Page 46: Best Practices für TDD in JavaScript

Timers

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

Bieten Kontrolle über Datum und Zeit.

Page 47: Best Practices für TDD in JavaScript

Timers

Page 48: Best Practices für TDD in JavaScript

Server

cre8tive / pixelio.de

Page 49: Best Practices für TDD in JavaScript

Server

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

Kontrolle über die Antworten des Fake Servers.

Page 50: Best Practices für TDD in JavaScript

Server

Page 51: Best Practices für TDD in JavaScript

Fixtures

Harald Wanetschka / pixelio.de

Page 52: Best Practices für TDD in JavaScript

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.

Page 53: Best Practices für TDD in JavaScript

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); });

Page 54: Best Practices für TDD in JavaScript

TDD in bestehenden Projekten?:

S. Hofschlaeger / pixelio.de

Page 55: Best Practices für TDD in JavaScript

Nicht alle Tests auf einmal nachziehen!

Stephan Bratek/geralt / pixelio.de

Page 56: Best Practices für TDD in JavaScript

Schnittstellen schaffen

Tim Reckmann / pixelio.de

Page 57: Best Practices für TDD in JavaScript

Boy Scout Rule

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

piu700 / pixelio.de

Page 58: Best Practices für TDD in JavaScript

Fragen?

Rainer Sturm / pixelio.de

Page 59: Best Practices für TDD in JavaScript

KONTAKT

Sebastian Springer [email protected]

Mayflower GmbH Mannhardtstr. 6 80538 München Deutschland

@basti_springer

https://github.com/sspringer82