Back To The Front - Javascript Test Driven Development is between us (workshop)

Preview:

DESCRIPTION

Javascript & browsers have been for years a complex and unsafe environment for a web developer, now we have the right tools to gain control on what we are distributing in our web applications. During the workshop you will learn first-hand basic Javascript Test Driven Development practices including testing, refactoring and related agile practices such as continuous integration and pair programming. presented at italian Back To The Front conference /w @sirLisko

Citation preview

back to the frontjavascript tdd is between us

Who are we?Marco Cedaro javascript pirate, arr

Spreaker Frontend Developer

Luca Lischetti8-bit lover and super hero

(almost) Shazam Frontend Developer

Who are we?Marco Cedaro javascript pirate, arr

Spreaker Frontend Developer

Luca Lischetti8-bit lover and super hero

(almost) Shazam Frontend Developer

The content of this workshop do not necessarily reflect the opinion of authors employers

Who are we?Marco Cedaro javascript pirate, arr

Spreaker Frontend Developer

Luca Lischetti8-bit lover and super hero

(almost) Shazam Frontend Developer

The content of this workshop do not necessarily reflect the opinion of authors employersAuthors employers are not responsible in any way of authors bad coding and funny spoken english

I believe I can fly

Fearless & Unconscious

Fearless & Unconscious

But Life Goes On

TDD is about control

and about DESIGN too

The curious case of JavaScript unit testing

Unit Test and Functional Test

Pro/Constest

interaction between libraries

control over

codebase

consistency against

external changes

execution time

test integration

That's the browsers, baby

Unit testing is supposed to test a single atomic “unit”

of functionality without

dependencies on anything else

This is where you start to run into

serious dependency problems due to the interrelation HTML

and CSS

Unit testing is supposed to test a single atomic “unit”

of functionality without

dependencies on anything else

This is where you start to run into

serious dependency problems due to the interrelation HTML

and CSS

Unit testing is supposed to test a single atomic “unit”

of functionality without

dependencies on anything else

What do you test? Usually how the user interface responds

to user input. Actually, the realm of

functional testing

How does it work?

have you seen LOST?

Write a new test

Write a new test

Run test & let it fail

Write a new test

Run test & let it fail

Write the code

Write a new test

Run test & let it fail

Write the code

Run test & let it succeed

Write a new test

Refactor your code

Run test & let it fail

Write the code

Run test & let it succeed

Coding

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

pub.. What?

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$.watch("customEvent", function(obj) {! //DO STUFF});

_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });

_$.watch("customEvent", function(obj) {! //DO STUFF});

_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$.watch("customEvent", function(obj) {! //DO STUFF});

_$.notify("customEvent");_$.notify("customEvent", { prop : "value" });

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function (_) {

return {

notify: function (a, b, c, d) { for (d = -1, c = [].concat(_[a]); c[++d];) c[d](b);

}, watch: function (a, b) {

(_[a] || (_[a] = [])).push(b); }

}

})({});

[...]

testNotify: function(){!!! ! var a = 0;!! ! _$.watch('testNotify', function(){ a = 1; });! ! _$.notify('testNotify');

! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! },[...]

[...]

testNotify: function(){!!! ! var a = 0;!! ! _$.watch('testNotify', function(){ a = 1; });! ! _$.notify('testNotify');

! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! },[...]

[...]

testNotify: function(){!!! ! var a = 0;!! ! _$.watch('testNotify', function(){ a = 1; });! ! _$.notify('testNotify');

! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! },[...]

[...]

testNotify: function(){!!! ! var a = 0;!! ! _$.watch('testNotify', function(){ a = 1; });! ! _$.notify('testNotify');

! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! },[...]

[...]

testNotify: function(){!!! ! var a = 0;!! ! _$.watch('testNotify', function(){ a = 1; });! ! _$.notify('testNotify');

! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! },[...]

[...]

testNotify: function(){!!! ! var a = 0;!! ! _$.watch('testNotify', function(){ a = 1; });! ! _$.notify('testNotify');

! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! },[...]

[...]

testNotify: function(){!!! ! var a = 0;!! ! _$.watch('testNotify', function(){ a = 1; });! ! _$.notify('testNotify');

! ! assertEquals(1, a);! },! ! !! testNotifyWithMemo: function(){! !! ! var a = 0 ;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! },[...]

[...]

! testNotifyWithMultipleWhatches: function(){!!! ! var a = 0, b = 0;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.watch('testNotify', function(memo){ b = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]

[...]

! testNotifyWithMultipleWhatches: function(){!!! ! var a = 0, b = 0;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.watch('testNotify', function(memo){ b = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]

[...]

! testNotifyWithMultipleWhatches: function(){!!! ! var a = 0, b = 0;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.watch('testNotify', function(memo){ b = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; } ! ! ! ! else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

[...]

testNotifyWithMultipleWhatchesNested: function(){!!! ! var a = 0, b = 0, c=0;!! ! _$.watch('testNotify', function(memo){

a = memo.test;! ! ! _$.watch('testNotify', function(memo){! ! ! ! if (b<2){ b = memo.test; }

else { c = memo.test; }! ! ! });! ! });! !! ! _$.notify('testNotify', {test: 1});! ! assertEquals(1, a);! ! assertEquals(0, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 2});! ! assertEquals(2, a);! ! assertEquals(2, b);! ! assertEquals(0, c);! !! ! _$.notify('testNotify', {test: 3});! ! assertEquals(3, a);! ! assertEquals(2, b);! ! assertEquals(3, c);! },[...]

There's no parachute

There's a major bug, let's take another look at the code...

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }! ! ! registered[event].push(handler);! ! }

};})();

There's no parachute

How would you write a test to check it?

[...]

! testNotifyWithMultipleWhatches: function(){!!! ! var a = 0, b = 0;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.watch('testNotify', function(memo){ b = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]

[...]

! testWhatchWithoutHandler: function(){! !! ! var a = 0, b = 0;!! ! _$.watch('testNotify', function(memo){ a = memo.test; });! ! _$.watch('testNotify');! ! _$.watch('testNotify', function(memo){ b = memo.test; });! ! _$.notify('testNotify', {test: 1});

! ! assertEquals(1, a);! ! assertEquals(1, b);! },[...]

_$ = (function() {var registered = {};

return {

! ! notify: function(event, memo) {! ! ! if (registered[event] instanceof Array){! ! ! ! var handlers = [].concat(registered[event]);

! ! ! ! for (var i=0, handler; (handler = handlers[i]); i++){! ! ! ! ! handler.call(this, memo);! ! ! ! }! ! ! }! ! },! ! !! ! watch: function(event, handler) {! ! ! if (handler) {

if (typeof registered[event] === "undefined"){! ! ! ! registered[event] = [];! ! ! }

! ! ! registered[event].push(handler);}

! ! }

};})();

_$ = (function (_) {

return {

notify: function (a, b, c, d) { for (d = -1, c = [].concat(_[a]); c[++d];) c[d](b); },

watch: function (a, b) { if(b)(_[a] || (_[a] = [])).push(b); }

}

})({});

Spy, Stub & Mock

Fake objects & methods

Sinon.js

a function that records arguments,

return value, the value of this and

exception thrown (if any) for all its calls.

Spies

functions (spies) with pre-programmed

behavior.

Stuba function that

records arguments, return value, the value of this and

exception thrown (if any) for all its calls.

Spies

functions (spies) with pre-programmed

behavior.

Stuba function that

records arguments, return value, the value of this and

exception thrown (if any) for all its calls.

Spiesfunctions (spies) with

pre-programmed behavior (stubs) as

well as pre-programmed expectations.

Mock

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SysyemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

"test MyLib Registers To 'SystemOn' Event": function(){! !! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },! ! !! testMyLibLoggedNotLogged: function(){! !! ! var stub = sinon.stub(User, 'isLogged');!! ! stub.returns(true);! ! //DO STUFF && ASSERTIONS

! ! stub.returns(false);! ! //DO STUFF && ASSERTIONS

! },[...]

[...]

testMyLibRegistersToSystemOnEvent: function(){!!! ! var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! assertTrue(spy.calledWith('SystemOn'));! },[...]

[...]

//testMyLibRegistersToSystemOnEvent: function(){! !! ! //var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! //assertTrue(spy.calledWith('SysyemOn'));! //},!! testMyLibRegistersToSystemOnEvent: function(){!!! ! var mock = sinon.mock(_$);! ! mock.expect('watch').calledWith('SysyemOn');! ! //DO STUFF TO INIT YOUR LIB! ! mock.verify();! },[...]

[...]

//testMyLibRegistersToSystemOnEvent: function(){! !! ! //var spy = sinon.spy(_$, 'watch');!! ! //DO STUFF TO INIT YOUR LIB! ! //assertTrue(spy.calledWith('SysyemOn'));! //},!! testMyLibRegistersToSystemOnEvent: function(){!!! ! var mock = sinon.mock(_$);! ! mock.expect('watch').calledWith('SysyemOn');! ! //DO STUFF TO INIT YOUR LIB! ! mock.verify();! },[...]

What are we going to do

cheat

config

dist

libs

tools

workshop

build.sh

build.bat

readme.txt

svn co https://svn.dsgn.it/jstdd

cheat

config

dist

libs

tools

workshop

build.sh

build.bat

readme.txt

/config/browsers.prop

build.xml

default.prop

svn co https://svn.dsgn.it/jstdd

/libs/base.js

sinon.js

cheat

config

dist

libs

tools

workshop

build.sh

build.bat

readme.txt

/config/browsers.prop

build.xml

default.prop

svn co https://svn.dsgn.it/jstdd

/libs/base.js

sinon.js

cheat

config

dist

libs

tools

workshop

build.sh

build.bat

readme.txt

/tools/ant

browser

jslint

JsTestDriver

/config/browsers.prop

build.xml

default.prop

svn co https://svn.dsgn.it/jstdd

/libs/base.js

sinon.js

cheat

config

dist

libs

tools

workshop

build.sh

build.bat

readme.txt

/tools/ant

browser

jslint

JsTestDriver

/workshop/append

find

syntax

/config/browsers.prop

build.xml

default.prop

svn co https://svn.dsgn.it/jstdd

/libs/base.js

sinon.js

cheat

config

dist

libs

tools

workshop

build.sh

build.bat

readme.txt

/tools/ant

browser

jslint

JsTestDriver

/workshop/append

find

syntax

/append/src

append.js

test

append.test.js

jsTestDriver.conf

/config/browsers.prop

build.xml

default.prop

svn co https://svn.dsgn.it/jstdd

/libs/base.js

sinon.js

cheat

config

dist

libs

tools

workshop

build.sh

build.bat

readme.txt

/tools/ant

browser

jslint

JsTestDriver

/workshop/append

find

syntax

/append/src

append.js

test

append.test.js

jsTestDriver.conf

/config/browsers.prop

build.xml

default.prop

svn co https://svn.dsgn.it/jstdd

build append | sh build.sh append

A simple modular event driven appa javascript code highlighter with 3 components:

syntax highlighterselected word highlighterfinder

First step - syntaxwatch a SRC_LOADED event with the memo {file:'{{FILE_SRC}}'}

highlight (wrap in a span with the keyword itself as classname)

function => <span class="function">function</span>

var, if ... else, for, return, ...

notify SRC_READY with the memo {file:'{{EDITED_SRC}}'}

Second step - appendwatch previous SRC_READY

create a div[id="src_container"] and fill it with the source

append to document

llisten dblclick event and notify SRC_HIGHLIGHT with this memo: {keyword:'{{HIGHLIGHTED_WORD}}'}

manage the SRC_HIGHLIGHT notification <span class="highlight">{{HIGHLIGHTED_KEYWORKD}}</span>

Third step - findwatch to SRC_READY

create a div[id=form] and append inside of it

input[type=text][id=search]

input[type=button][id=submitBtn][value=Search]

append the div to the document

on click on submit notify a SRC_HIGHLIGHT event with memo {keyword:"{{SEARCHED_TEXT}}"}

Where are we now?

Costs of change

I Build So Consistently

I Build So Consistently

Identify Build

Share make it Continuous