Test and Behavior Driven Development in JavaScript Steven Parkes - - PowerPoint PPT Presentation

test and behavior driven development in javascript
SMART_READER_LITE
LIVE PREVIEW

Test and Behavior Driven Development in JavaScript Steven Parkes - - PowerPoint PPT Presentation

Test and Behavior Driven Development in JavaScript Steven Parkes smparkes@smparkes.net JavaScript means Different things to Different People <FORM NAME=FORM> <TABLE BORDER CELLPADDING=3> <TR>


slide-1
SLIDE 1

Test and Behavior Driven Development in JavaScript

Steven Parkes smparkes@smparkes.net

slide-2
SLIDE 2

“JavaScript” means Different things to Different People

slide-3
SLIDE 3

<FORM NAME=‘FORM’> <TABLE BORDER CELLPADDING=3> <TR> <TD><NOBR>radius: <INPUT NAME="Circle_radius" SIZE=4></NOBR></TD> <TD><INPUT TYPE=BUTTON onclick="document.FORM.submit();" VALUE="calculate"></TD> <TD ALIGN=RIGHT BGCOLOR="#AACCFF"> <NOBR>circumference: <INPUT NAME="Circle_circumference" SIZE=9></NOBR><BR> <NOBR>area: <INPUT NAME="Circle_area" SIZE=9></NOBR></TD> </TR> </TABLE> </FORM>

slide-4
SLIDE 4

var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(8124, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8124/');

slide-5
SLIDE 5
slide-6
SLIDE 6

Canvas Offline Applications Local Storage Video XMLHttpRequest SVG “HTML5”

slide-7
SLIDE 7

ChessFlock

slide-8
SLIDE 8

XMPP

slide-9
SLIDE 9

ejabberd xmpp xmpp xmpp

slide-10
SLIDE 10

ejabberd http kibosh http http kibosh kibosh

slide-11
SLIDE 11

ejabberd: XMPP server written in Erlang kibosh: XMPP BOSH connection manager written in Ruby strophejs: XMPP BOSH client written in JavaScript dramatis: actor library written in JavaScript/Ruby/Python chessflock: chess client/server written in JavaScript/HTML/SVG Server Client

slide-12
SLIDE 12

Caveats

slide-13
SLIDE 13

Incomplete

slide-14
SLIDE 14

Low test coverage

slide-15
SLIDE 15

Egregious stupidity

slide-16
SLIDE 16

Dramatis testbed

slide-17
SLIDE 17

“throw one away”

slide-18
SLIDE 18

Test Driven Development

slide-19
SLIDE 19

s/Test/Behavior/

slide-20
SLIDE 20

Test Driven Development

slide-21
SLIDE 21

Test Driven Development

slide-22
SLIDE 22

Test Driven Development

slide-23
SLIDE 23

Test Driven Development

slide-24
SLIDE 24

Write the tests first

slide-25
SLIDE 25

Red Green Refactor

slide-26
SLIDE 26

Flavors of tests

slide-27
SLIDE 27

Unit

slide-28
SLIDE 28

Irreducible

slide-29
SLIDE 29

Monolingual

slide-30
SLIDE 30

Integration

Multiple units

Maybe multiple languages

slide-31
SLIDE 31

System

slide-32
SLIDE 32

Unit Monolingual (JavaScript) Integration Developer focused

slide-33
SLIDE 33

What makes JavaScript so special?

slide-34
SLIDE 34

Standard Library

  • r lack thereof
slide-35
SLIDE 35

Object Array Function Date String Regex

slide-36
SLIDE 36

Asynchronous no wait no sleep not threads

slide-37
SLIDE 37

Asynchronous no wait no sleep no threads setTimeout

slide-38
SLIDE 38

Asynchronous no wait no sleep no threads window.setTimeout

slide-39
SLIDE 39

Asynchronous no wait no sleep no threads window.setTimeout

slide-40
SLIDE 40

Asynchronous no wait no sleep no Threads window.setTimeout I/O

slide-41
SLIDE 41

Asynchronous no wait no sleep no threads window.setTimeout I/O Packaging

slide-42
SLIDE 42

Asynchronous no wait no sleep no threads window.setTimeout I/O Packaging SpiderMonkey v8 SquirrelFish Rhino

slide-43
SLIDE 43

Object Array Function Date String Regex Need more than this to run tests

slide-44
SLIDE 44

Beyond ES5

slide-45
SLIDE 45

CommonJS

slide-46
SLIDE 46

“HTML5”

slide-47
SLIDE 47

<HTML> <HEAD> <TITLE></TITLE> </HEAD> <BODY> </BODY> </HTML>

slide-48
SLIDE 48

window

slide-49
SLIDE 49

window.setTimeout

slide-50
SLIDE 50

window.setTimeout Timers / events / event loops

slide-51
SLIDE 51

window.XMLHttpRequest

slide-52
SLIDE 52

HTML5 implementations

slide-53
SLIDE 53

Browsers, of course but also ...

slide-54
SLIDE 54

HTMLUnit Java Rhino

slide-55
SLIDE 55

Env.js “mostly” JavaScript Rhino/SpiderMonkey

slide-56
SLIDE 56

JavaScript TDD/BDD vs ...

slide-57
SLIDE 57

Dynamically typed Loose scoping No protection levels

slide-58
SLIDE 58

Highly asynchronous Lots of callbacks Lots of async callbacks

slide-59
SLIDE 59

JavaScript TDD and BDD Frameworks

slide-60
SLIDE 60

But first ...

slide-61
SLIDE 61

JSLint

slide-62
SLIDE 62

JavaScript TDD: qunit

slide-63
SLIDE 63

test("module without setup/teardown (default)", function() { expect(1);

  • k(true);

});

slide-64
SLIDE 64

test("module without setup/teardown (default)", function() { expect(1);

  • k(true);

});

  • k()

equals() same()

slide-65
SLIDE 65

test("module without setup/teardown (default)", function() { expect(1);

  • k(true);

});

  • k()

equals() same() module() setup() teardown()

slide-66
SLIDE 66

test("runnable callbacks are run later with timeout of 0", function() { expect(2); var occurred = 0; setTimeout(function(){

  • ccurred = Date.now();

}, 0);

  • k( occurred === 0, "Timeout callback was not executed immediately" );

setTimeout(function(){

  • k( occurred !== 0, "Timeout callback executed" );

start(); },100); stop(); });

Asynch test: test is not complete at the end of the function body

slide-67
SLIDE 67

test("runnable callbacks are run later with timeout of 0", function() { expect(2); var occurred = 0; setTimeout(function(){

  • ccurred = Date.now();

}, 0);

  • k( occurred === 0, "Timeout callback was not executed immediately" );

setTimeout(function(){

  • k( occurred !== 0, "Timeout callback executed" );

start(); },100); stop(); });

stop()

slide-68
SLIDE 68

test("runnable callbacks are run later with timeout of 0", function() { expect(2); var occurred = 0; setTimeout(function(){

  • ccurred = Date.now();

}, 0);

  • k( occurred === 0, "Timeout callback was not executed immediately" );

setTimeout(function(){

  • k( occurred !== 0, "Timeout callback executed" );

start(); },100); stop(); });

incomplete()

slide-69
SLIDE 69

test("runnable callbacks are run later with timeout of 0", function() { expect(2); var occurred = 0; setTimeout(function(){

  • ccurred = Date.now();

}, 0);

  • k( occurred === 0, "Timeout callback was not executed immediately" );

setTimeout(function(){

  • k( occurred !== 0, "Timeout callback executed" );

start(); },100); stop(); });

start()

slide-70
SLIDE 70

test("runnable callbacks are run later with timeout of 0", function() { expect(2); var occurred = 0; setTimeout(function(){

  • ccurred = Date.now();

}, 0);

  • k( occurred === 0, "Timeout callback was not executed immediately" );

setTimeout(function(){

  • k( occurred !== 0, "Timeout callback executed" );

start(); },100); stop(); });

complete()

slide-71
SLIDE 71

test("runnable callbacks are run later with timeout of 0", function() { expect(2); var occurred = 0; setTimeout(function(){

  • ccurred = Date.now();

}, 0);

  • k( occurred === 0, "Timeout callback was not executed immediately" );

setTimeout(function(){

  • k( occurred !== 0, "Timeout callback executed" );

start(); },100); stop(); });

expect(n)

slide-72
SLIDE 72

What if start() is never called?

slide-73
SLIDE 73

JavaScript BDD: Jasmine

slide-74
SLIDE 74

it("should return the game to the players",function(){ var tom = { player_name: function(){ return "tom"; }}; var jerry = { player_name: function(){ return "tom"; }}; var toms_game; var jerrys_game; this.server.join(tom, function(game) { toms_game = game; if (jerrys_game) { expect(toms_game).toBe(jerrys_game); complete(); } }); this.server.join(jerry, function(game) { jerrys_game = game; if (toms_game) { expect(toms_game).toBe(jerrys_game); complete(); } }); incomplete(); });

it(...) expect(...).toSomething() complete/incomplete(...)

slide-75
SLIDE 75

Ready, Set, Go ...

slide-76
SLIDE 76

Uh ...

slide-77
SLIDE 77

Fixtures

slide-78
SLIDE 78

<!DOCTYPE html> <html> <head> <title>QUnit Test Suite</title> <link rel="stylesheet" href="../qunit/qunit.css" type="text/css" media="screen"> <script type="text/javascript" src="../qunit/qunit.js"></script> <script type="text/javascript" src="test.js"></script> <script type="text/javascript" src="same.js"></script> </head> <body> <h1 id="qunit-header">QUnit Test Suite</h1> <h2 id="qunit-banner"></h2> <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> </body> </html>

slide-79
SLIDE 79

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Jasmine Test Runner</title> <script type="text/javascript"> // yes, really keep this here to keep us honest, but only for jasmine's own runner! [xw] undefined = "diz be undefined yo"; </script> <script type="text/javascript" src="../src/base.js"></script> <script type="text/javascript" src="../src/util.js"></script> <script type="text/javascript" src="../src/Env.js"></script> <script type="text/javascript" src="../src/Reporter.js"></script> <script type="text/javascript" src="../src/Block.js"></script> <script type="text/javascript" src="../src/JsApiReporter.js"></script> <script type="text/javascript" src="../src/Matchers.js"></script> <script type="text/javascript" src="../src/mock-timeout.js"></script> <script type="text/javascript" src="../src/MultiReporter.js"></script> <script type="text/javascript" src="../src/NestedResults.js"></script> <script type="text/javascript" src="../src/PrettyPrinter.js"></script> <script type="text/javascript" src="../src/Queue.js"></script> <script type="text/javascript" src="../src/Runner.js"></script> <script type="text/javascript" src="../src/Spec.js"></script> <script type="text/javascript" src="../src/Suite.js"></script> <script type="text/javascript" src="../src/WaitsBlock.js"></script> <script type="text/javascript" src="../src/WaitsForBlock.js"></script> <script type="text/javascript" src="../src/html/TrivialReporter.js"></script> <script type="text/javascript" src="suites/BaseSpec.js"></script> <script type="text/javascript" src="suites/CustomMatchersSpec.js"></script> <script type="text/javascript" src="suites/EnvSpec.js"></script> <script type="text/javascript" src="suites/ExceptionsSpec.js"></script> <script type="text/javascript" src="suites/JsApiReporterSpec.js"></script> <script type="text/javascript" src="suites/MatchersSpec.js"></script> <script type="text/javascript" src="suites/MockClockSpec.js"></script>

slide-80
SLIDE 80

<script type="text/javascript" src="suites/MatchersSpec.js"></script> <script type="text/javascript" src="suites/MockClockSpec.js"></script> <script type="text/javascript" src="suites/MultiReporterSpec.js"></script> <script type="text/javascript" src="suites/NestedResultsSpec.js"></script> <script type="text/javascript" src="suites/PrettyPrintSpec.js"></script> <script type="text/javascript" src="suites/ReporterSpec.js"></script> <script type="text/javascript" src="suites/RunnerSpec.js"></script> <script type="text/javascript" src="suites/QueueSpec.js"></script> <script type="text/javascript" src="suites/SpecSpec.js"></script> <script type="text/javascript" src="suites/SpecRunningSpec.js"></script> <script type="text/javascript" src="suites/SpySpec.js"></script> <script type="text/javascript" src="suites/SuiteSpec.js"></script> <script type="text/javascript" src="suites/TrivialReporterSpec.js"></script> <script type="text/javascript" src="suites/WaitsForBlockSpec.js"></script> <script type="text/javascript"> (function() { var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; var trivialReporter = new jasmine.TrivialReporter(); jasmineEnv.addReporter(trivialReporter); jasmineEnv.specFilter = function(spec) { return trivialReporter.specFilter(spec); }; window.onload = function() { jasmineEnv.execute(); }; })(); </script> <link href="../src/html/jasmine.css" rel="stylesheet"/> </head> <body> </body> </html>

slide-81
SLIDE 81

Test Runners : What, Where, How, When, Why

slide-82
SLIDE 82

Test Runners : What, Where, How, When

slide-83
SLIDE 83

Without tests, code won’t work

slide-84
SLIDE 84

Without tests, code won’t work If writing tests is hard, they won’t get written

slide-85
SLIDE 85

Without tests, code won’t work If writing tests is hard, they won’t get written If running tests is had, they won’t get run

slide-86
SLIDE 86

Without tests, code won’t work If writing tests is hard, they won’t get written If running tests is had, they won’t get run If tests don’t get run, they won’t get written

slide-87
SLIDE 87

Without tests, code won’t work If writing tests is hard, they won’t get written If running tests is had, they won’t get run If tests don’t get run, they won’t get written Without tests, code won’t work

slide-88
SLIDE 88

Make the simple things easy and the hard things possible

slide-89
SLIDE 89

DOM vs non-DOM Synch vs Asynch

slide-90
SLIDE 90

Make the hard things possible

slide-91
SLIDE 91

Make the hard things possible and everything else equally hard

slide-92
SLIDE 92

Write a test Switch to browser Load a test page Find the/a failure Switch to editor Write code Switch to browser Hit refresh Find the/a success Lather, rinse, repeat

slide-93
SLIDE 93

This sucks

slide-94
SLIDE 94

Let me count the ways

slide-95
SLIDE 95

Load what?

slide-96
SLIDE 96

From where?

slide-97
SLIDE 97

How, i.e., with what?

slide-98
SLIDE 98

When?

slide-99
SLIDE 99

What: pick your fixture(s) Where: run some server somewhere How: with the browser on my desktop (repeat as necessary) When: when I feel like it

slide-100
SLIDE 100

Did I mention this sucks?

slide-101
SLIDE 101

What: I hate fixtures

slide-102
SLIDE 102

They’re painful to setup They don’t reflect the real world

slide-103
SLIDE 103

In vitro In situ In vivo

slide-104
SLIDE 104

Jazz: a test runner

slide-105
SLIDE 105

Jazz Supports Jasmine and qunit Supports browsers (Chrome, Firefox) Supports env.js (Ruby/SpiderMonkey) Can run fixture-less tests Integrates with wake

slide-106
SLIDE 106

Wake: make/rake + autotest/watchr

slide-107
SLIDE 107

Save a file, run a test

slide-108
SLIDE 108

Save a file, run tests

slide-109
SLIDE 109

Understands dependences Integrated with jazz/envjs

slide-110
SLIDE 110

Records successes/failures Re-executes: Changed files until success Failed tests until success All tests

slide-111
SLIDE 111

Plugin Architecture Matching source files Result files Dependences Success/failure

slide-112
SLIDE 112

Plugins haml/sass jazz jslintrb shell graphics (inkscape, batik, rsvg) cache_manifest

slide-113
SLIDE 113

qunit: http://github.com/jquery/qunit Jasmine: http://github.com/pivotal/jasmine my fork: http://github.com/smparkes/jasmine jslintrb: http://github.com/smparkes/jslintrb jazz: http://github.com/smparkes/jazz wake: http://github.com/smparkes/wake ttt: http://github.com/smparkes/ttt chessflock: http://github.com/smparkes/chessflock