SlideShare a Scribd company logo
1 of 110
Unittesting JavaScript
   with Evidence
        Tobie Langel
      @tobie on twitter
ā€œEvidence in its broadest
sense includes everything
that is used to determine
or demonstrate the truth
of an assertion.ā€
               ā€“Wikipedia
http://github.com/tobie/evidence
Yet Another Unit Test
     Framework
DOH           Test.Simple/More
JSUnit        TestCase
QUnit         jsUnitTest
Crosscheck    JSTest
J3Unit        jsUnity
JSNUnit       RhinoUnit
YUI Test      FireUnit
JSSpec        ...
unittest.js
JSpec
screw-unit
Why?
Scratching our own itch
A lot of unit tests (> 2000 assertions).
A lot of unit tests (> 2000 assertions).
Complex cases (async, cross-browser).
A lot of unit tests (> 2000 assertions).
Complex cases (async, cross-browser).
Uses a framework originally created in
2005 for script.aculo.us...
A lot of unit tests (> 2000 assertions).
Complex cases (async, cross-browser).
Uses a framework originally created in
2005 for script.aculo.us...
...with a dependency on Prototype.
#1
Framework agnostic
#2
Environment agnostic
#2
Environment agnostic
 (Attempts to run in as many different
      environments as possible.)
#3
Self-contained
#3
   Self-contained
(Doesnā€™t pollute the global scope.)
#4
Reliable
#5
Built with async in mind
#6
Easier to automate
#7
Promotes better testing
ā€œI recommend that
developers spend
25-50% of their time
developing tests.ā€
              ā€“Kent Beck
Drawing by Witold Riedel (http://www.witoldriedel.com)
Kent Beckā€™s original
   white paper


                Drawing by Witold Riedel (http://www.witoldriedel.com)
Kent Beckā€™s original
   white paper
    http://tr.im/kentbeck



                            Drawing by Witold Riedel (http://www.witoldriedel.com)
JUnit
JUnit
Kent Beck &
Erich Gamma
Test::Unit
MiniTest
unittest / PyUnit
YUI.Test
... and of course, Kent
Beckā€™s original Smalltalk
      implementation.
(
Evidence
!=
RSpec
BDD
Why?
I donā€™t like BDD
&
JavaScriptā€™s awful at
    writing DSLs
unless
youā€™re using itā€™s bad
       parts!
function callAssertions(fn) {
 var rgxp = /^[^{]*{((.*n*)*)}/m;
 fn = fn.toString();
 fn = fn.match(rgxp)[1];

    eval('with (dsl) { 
     with (context) { 
     with (matchers) { ' +
     fn + ' }}}');
}
Function decompilation
function callAssertions(fn) {
                      (Not part of any standard.)
 var rgxp = /^[^{]*{((.*n*)*)}/m;
 fn = fn.toString();
 fn = fn.match(rgxp)[1];

    eval('with (dsl) { 
     with (context) { 
     with (matchers) { ' +
     fn + ' }}}');
}
function callAssertions(fn) {
 var rgxp = /^[^{]*{((.*n*)*)}/m;
 fn = fn.toString();
 fn = fn.match(rgxp)[1];

    eval('with (dsl) { 
     with (context) { 
     with (matchers) { ' +
     fn + ' }}}'); Nested with statements
}                      (Not ES5 strict compliant.)
function callAssertions(fn) {
 var rgxp = /^[^{]*{((.*n*)*)}/m;
 fn = fn.toString();
                          eval
 fn = fn.match(rgxp)[1];
           (Not supported in some environments.)



    eval('with (dsl) { 
     with (context) { 
     with (matchers) { ' +
     fn + ' }}}');
}
function callAssertions(fn) {
 var rgxp = /^[^{]*{((.*n*)*)}/m;
 fn = fn.toString();
 fn = fn.match(rgxp)[1];

    eval('with (dsl) { 
     with (context) { 
     with (matchers) { ' +
     fn + ' }}}');
}
)
xUnit
xUnit
xUnit
TestCase
xUnit
TestCase
TestSuite
xUnit
TestCase
TestSuite
TestRunner
xUnit
TestCase
TestSuite
TestRunner
TestResult
Anatomy of a TestCase
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 testFirst: function() {
   this.assertEqual('foo', _.ļ¬rst(this.array));
   this.assertUndeļ¬ned(_.ļ¬rst([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndeļ¬ned(_.last([]));
  }
});
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },
                              ā€œSubclassā€ TestCase
 testFirst: function() {
   this.assertEqual('foo', _.ļ¬rst(this.array));
   this.assertUndeļ¬ned(_.ļ¬rst([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndeļ¬ned(_.last([]));
  }
});
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },
                                           Give it a name.
 testFirst: function() {
   this.assertEqual('foo', _.ļ¬rst(this.array));
   this.assertUndeļ¬ned(_.ļ¬rst([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndeļ¬ned(_.last([]));
  }
});
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 testFirst: function() {
Add ļ¬xtures to your _.ļ¬rst(this.array));
   this.assertEqual('foo',
   setUp method.
   this.assertUndeļ¬ned(_.ļ¬rst([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndeļ¬ned(_.last([]));
  }
});
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
Preļ¬x setUp:testcases {with
      your function()
        this.array = ['foo', 'bar', 'baz'];
      },    test.
       testFirst: function() {
         this.assertEqual('foo', _.ļ¬rst(this.array));
         this.assertUndeļ¬ned(_.ļ¬rst([]));
       },

       testLast: function() {
         this.assertEqual('bar', _.last(this.array),
          'Failed to grab the last element of the array.');
         this.assertUndeļ¬ned(_.last([]));
       }
     });
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 testFirst: function() {
   this.assertEqual('foo', _.ļ¬rst(this.array));
   this.assertUndeļ¬ned(_.ļ¬rst([]));
 },

  testLast: your assertions.
    Write function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndeļ¬ned(_.last([]));
  }
});
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 testFirst: function() {
   this.assertEqual('foo', _.ļ¬rst(this.array));
   this.assertUndeļ¬ned(_.ļ¬rst([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndeļ¬ned(_.last([]));
  }
                             Optionally add
});                         meaningful error
                               messages.
Built-in async handling.
Evidence.TestCase.extend('AjaxTest', {
  testAjaxRequest: function(testcase) {
    testcase.pause();
    new Ajax.Request('/some/url', {
      onComplete: function(response) {
        testcase.resume(function() {
          this.assert(response);
        });
      }
    });
  }
});
Evidence.TestCase.extend('AjaxTest', {
  testAjaxRequest: function(testcase) {
    testcase.pause();
    new Ajax.Request('/some/url', { instance
                              TestCase
      onComplete: function(response) {
                           conveniently passed as a
        testcase.resume(function() {
          this.assert(response); ļ¬rst argument.
        });
      }
    });
  }
});
Evidence.TestCase.extend('AjaxTest', {
    testAjaxRequest: function(testcase) {
No need to bind this or
      testcase.pause();
create your own closure.
      new Ajax.Request('/some/url', {
        onComplete: function(response) {
          testcase.resume(function() {
            this.assert(response);
          });
        }
      });
    }
  });
Evidence.TestCase.extend('AjaxTest', {
  testAjaxRequest: function(testcase) {
    testcase.pause();
    new Ajax.Request('/some/url', {
      onComplete: function(response) {
        testcase.resume(function() {
          this.assert(response);
        });
      Bound to the original
      }
    });      scope so:
  } this === testcase
});
The TestSuite
function getTestCaseNames(testcaseClass) {
 var results = [];

    for (var property in testcaseClass.prototype) {
      if (property.indexOf('test') === 0) {
        results.push(property);
      }
    }

    return results.sort();
}
Grab all the methods
function getTestCaseNames(testcaseClass) {
 var results = [];        starting with test.

    for (var property in testcaseClass.prototype) {
      if (property.indexOf('test') === 0) {
        results.push(property);
      }
    }

    return results.sort();
}
function getTestCaseNames(testcaseClass) {
 var results = [];

    for (var property in testcaseClass.prototype) {
      if (property.indexOf('test') === 0) {
        results.push(property);
      }                              Sort the results.
    }

    return results.sort();
}
function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = getTestCaseNames(testcaseClass);

    for (var i = 0; i < methodNames.length; i++) {
      suite.push(new testcaseClass(methodNames[i]));
    }

    return suite;
}
Create a new suite.

function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = getTestCaseNames(testcaseClass);

    for (var i = 0; i < methodNames.length; i++) {
      suite.push(new testcaseClass(methodNames[i]));
    }

    return suite;
}
Give it a name.

function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = getTestCaseNames(testcaseClass);

    for (var i = 0; i < methodNames.length; i++) {
      suite.push(new testcaseClass(methodNames[i]));
    }

    return suite;
}
function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = getTestCaseNames(testcaseClass);

    for (var i = 0; i < methodNames.length; i++) {
      suite.push(new testcaseClass(methodNames[i]));
    }
                      Grab the testcase names.

    return suite;
}
function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = getTestCaseNames(testcaseClass);

    for (var i = 0; i < methodNames.length; i++) {
      suite.push(new testcaseClass(methodNames[i]));
    }

    return suite;   Create a new instance of
}
                       the testcase class.
function loadTestsFromTestCase(testcaseClass) {
  var suite = new TestSuite(testcaseClass.displayName),
     methodNames = getTestCaseNames(testcaseClass);

  for (var i = 0; i < methodNames.length; i++) {
    suite.push(new testcaseClass(methodNames[i]));
  }

  return suite;
Add it to your suite.
 }
Beneļ¬ts
Custom assertions
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },

 testElementExtend: function() {
   var extended = Element.extend(this.element);
   this.assertEqual(this.element, extended);
   this.assertRespondsTo('show', extended);
   // ...
 },

  testElementClone: function() {
    var cloned = Element.clone(this.element);
    this.assert(cloned.show); // is extended
    // ...
  }
});
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },
                                   What!?
 testElementExtend: function() {
   var extended = Element.extend(this.element);
   this.assertEqual(this.element, extended);
   this.assertRespondsTo('show', extended);
   // ...
 },

  testElementClone: function() {
    var cloned = Element.clone(this.element);
    this.assert(cloned.show); // is extended
    // ...
  }
});
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },

 testElementExtend: function() {
   var extended = Element.extend(this.element);
   this.assertEqual(this.element, extended);
   this.assertRespondsTo('show', extended);
   // ...
 },                           Again?!
  testElementClone: function() {
    var cloned = Element.clone(this.element);
    this.assert(cloned.show); // is extended
    // ...
  }
});
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },

 testElementExtend: function() {
   var extended = Element.extend(this.element);
   this.assertEqual(this.element, extended);
   this.assertRespondsTo('show', extended);
   // ...
 },

  testElementClone: function() {
    var cloned = Element.clone(this.element);
    this.assert(cloned.show); // is extended
    // ...
  OK, now I get it.
  }
});
!= DRY
Evidence.TestCase.extend('ElementTest', {
 //...

 assertElementExtended: function(element, message) {
   this._assertExpression(
     typeof element.show === 'function',
     message || 'Element is not extended.',
     'Expected %o to be extended.', element
   );
 },

 testElementExtend: function() {
   var extended = Element.extend(this.element);
   this.assertElementExtended(extended);
 },

 testElementClone: function() {
   var cloned = Element.clone(this.element);
   this.assertElementExtended(cloned);
 }
Evidence.TestCase.extend('ElementTest', {
 //...

 assertElementExtended: function(element, message) {
   this._assertExpression(
     typeof element.show === 'function',
                                      !
     message || 'Element is not extended.',
     'Expected %o to be extended.', element
   );
                         Syntax still subject to
 },                              change!
 testElementExtend: function() {
   var extended = Element.extend(this.element);
   this.assertElementExtended(extended);
 },

 testElementClone: function() {
   var cloned = Element.clone(this.element);
   this.assertElementExtended(cloned);
 }
Instance methods
function getInnerHTML(element) {
  var html = element.innerHTML.toString();
  return html.toLowerCase().gsub(/[rnt]/, '');
}

Evidence.TestCase.extend('ElementTest', {
 // ...

  testElementInsert: function() {
    var html = '<p>a paragraph</p>';
    Element.insert(this.element, html);
    this.assertEqual(html, getInnerHTML(this.element));
  }
});
function getInnerHTML(element) {
  var html = element.innerHTML.toString();
  return html.toLowerCase().gsub(/[rnt]/, '');
}
                                 Global scope pollution.
Evidence.TestCase.extend('ElementTest', {
 // ...

  testElementInsert: function() {
    var html = '<p>a paragraph</p>';
    Element.insert(this.element, html);
    this.assertEqual(html, getInnerHTML(this.element));
  }
});
function getInnerHTML(element) {
  var html = element.innerHTML.toString();
  return html.toLowerCase().gsub(/[rnt]/, '');
}         Obscure syntax.
        Whatā€™s wrong with
Evidence.TestCase.extend('ElementTest', {
 //this.element.innerHTML?
    ...

  testElementInsert: function() {
    var html = '<p>a paragraph</p>';
    Element.insert(this.element, html);
    this.assertEqual(html, getInnerHTML(this.element));
  }
});
Evidence.TestCase.extend('ElementTest', {
 // ...

 testElementInsert: function() {
   var html = '<p>a paragraph</p>';
   Element.insert(this.element, html);
   this.assertEqual(html, this.getElementHTML());
 },

  getElementHTML: function() {
    var html = this.element.innerHTML.toString();
    return html.toLowerCase().gsub(/[rnt]/, '');
  }
});
Evidence.TestCase.extend('ElementTest', {
 // ...
              Instance method!
 testElementInsert: function() {
   var html = '<p>a paragraph</p>';
   Element.insert(this.element, html);
   this.assertEqual(html, this.getElementHTML());
 },

  getElementHTML: function() {
    var html = this.element.innerHTML.toString();
    return html.toLowerCase().gsub(/[rnt]/, '');
  }
});
Evidence.TestCase.extend('ElementTest', {
 // ...

 testElementInsert: function() {
   var html = '<p>a paragraph</p>';
   Element.insert(this.element, html);
   this.assertEqual(html, this.getElementHTML());
 },

  getElementHTML: function() {
                 Would beneļ¬t from a
    var html = this.element.innerHTML.toString();
    return html.toLowerCase().gsub(/[rnt]/, '');
                custom assertion too.
  }
});
The TestRunner
var suite = loadTestsFromTestCase(ArrayTest);
var runner = new Evidence.Runner();
var results = runner.run(suite);
The TestResult
Logs to the console.




The TestResult
Logs to the console.




The TestResult

          Prints to STDOUT.
Displays the results in    Logs to the console.
     a web page.



               The TestResult

                          Prints to STDOUT.
Displays the results in    Logs to the console.
     a web page.



               The TestResult
    Sends the data back
  to the server in JSON
                          Prints to STDOUT.
        (or XML).
The Magic
All of this is good to know,
but itā€™s tedious.
So...
Evidence handles it for you!
Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 testFirst: function() {
   this.assertEqual('foo', _.ļ¬rst(this.array));
   this.assertUndeļ¬ned(_.ļ¬rst([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndeļ¬ned(_.last([]));
  }
});
Itā€™s all you need!
?
           @tobie on twitter

http://github.com/tobie/evidence
 (Soon hopefully on http://evidencejs.org)

More Related Content

What's hot

0003 es5 ķ•µģ‹¬ ģ •ė¦¬
0003 es5 ķ•µģ‹¬ ģ •ė¦¬0003 es5 ķ•µģ‹¬ ģ •ė¦¬
0003 es5 ķ•µģ‹¬ ģ •ė¦¬ģš±ėž˜ ź¹€
Ā 
Testing your javascript code with jasmine
Testing your javascript code with jasmineTesting your javascript code with jasmine
Testing your javascript code with jasmineRubyc Slides
Ā 
The Ring programming language version 1.7 book - Part 12 of 196
The Ring programming language version 1.7 book - Part 12 of 196The Ring programming language version 1.7 book - Part 12 of 196
The Ring programming language version 1.7 book - Part 12 of 196Mahmoud Samir Fayed
Ā 
Registro de venta
Registro de ventaRegistro de venta
Registro de ventalupe ga
Ā 
Unit-Testing Bad-Practices by Example
Unit-Testing Bad-Practices by ExampleUnit-Testing Bad-Practices by Example
Unit-Testing Bad-Practices by ExampleBenjamin Eberlei
Ā 
Unittesting Bad-Practices by Example
Unittesting Bad-Practices by ExampleUnittesting Bad-Practices by Example
Unittesting Bad-Practices by ExampleBenjamin Eberlei
Ā 
The Ring programming language version 1.5.1 book - Part 75 of 180
The Ring programming language version 1.5.1 book - Part 75 of 180The Ring programming language version 1.5.1 book - Part 75 of 180
The Ring programming language version 1.5.1 book - Part 75 of 180Mahmoud Samir Fayed
Ā 
Bad test, good test
Bad test, good testBad test, good test
Bad test, good testSeb Rose
Ā 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196Mahmoud Samir Fayed
Ā 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189Mahmoud Samir Fayed
Ā 
Transducers in JavaScript
Transducers in JavaScriptTransducers in JavaScript
Transducers in JavaScriptPavel Forkert
Ā 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner codeMite Mitreski
Ā 
The Ring programming language version 1.6 book - Part 11 of 189
The Ring programming language version 1.6 book - Part 11 of 189The Ring programming language version 1.6 book - Part 11 of 189
The Ring programming language version 1.6 book - Part 11 of 189Mahmoud Samir Fayed
Ā 
Logic Equations Resolver J Script
Logic Equations Resolver   J ScriptLogic Equations Resolver   J Script
Logic Equations Resolver J ScriptRoman Agaev
Ā 
Java script advance-auroskills (2)
Java script advance-auroskills (2)Java script advance-auroskills (2)
Java script advance-auroskills (2)BoneyGawande
Ā 
The Ring programming language version 1.6 book - Part 34 of 189
The Ring programming language version 1.6 book - Part 34 of 189The Ring programming language version 1.6 book - Part 34 of 189
The Ring programming language version 1.6 book - Part 34 of 189Mahmoud Samir Fayed
Ā 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React AlicanteIgnacio MartĆ­n
Ā 

What's hot (20)

Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
Ā 
0003 es5 ķ•µģ‹¬ ģ •ė¦¬
0003 es5 ķ•µģ‹¬ ģ •ė¦¬0003 es5 ķ•µģ‹¬ ģ •ė¦¬
0003 es5 ķ•µģ‹¬ ģ •ė¦¬
Ā 
Testing your javascript code with jasmine
Testing your javascript code with jasmineTesting your javascript code with jasmine
Testing your javascript code with jasmine
Ā 
The Ring programming language version 1.7 book - Part 12 of 196
The Ring programming language version 1.7 book - Part 12 of 196The Ring programming language version 1.7 book - Part 12 of 196
The Ring programming language version 1.7 book - Part 12 of 196
Ā 
Registro de venta
Registro de ventaRegistro de venta
Registro de venta
Ā 
Unit-Testing Bad-Practices by Example
Unit-Testing Bad-Practices by ExampleUnit-Testing Bad-Practices by Example
Unit-Testing Bad-Practices by Example
Ā 
Unittesting Bad-Practices by Example
Unittesting Bad-Practices by ExampleUnittesting Bad-Practices by Example
Unittesting Bad-Practices by Example
Ā 
Google guava
Google guavaGoogle guava
Google guava
Ā 
The Ring programming language version 1.5.1 book - Part 75 of 180
The Ring programming language version 1.5.1 book - Part 75 of 180The Ring programming language version 1.5.1 book - Part 75 of 180
The Ring programming language version 1.5.1 book - Part 75 of 180
Ā 
Bad test, good test
Bad test, good testBad test, good test
Bad test, good test
Ā 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196
Ā 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189
Ā 
Transducers in JavaScript
Transducers in JavaScriptTransducers in JavaScript
Transducers in JavaScript
Ā 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner code
Ā 
The Ring programming language version 1.6 book - Part 11 of 189
The Ring programming language version 1.6 book - Part 11 of 189The Ring programming language version 1.6 book - Part 11 of 189
The Ring programming language version 1.6 book - Part 11 of 189
Ā 
Logic Equations Resolver J Script
Logic Equations Resolver   J ScriptLogic Equations Resolver   J Script
Logic Equations Resolver J Script
Ā 
Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
Ā 
Java script advance-auroskills (2)
Java script advance-auroskills (2)Java script advance-auroskills (2)
Java script advance-auroskills (2)
Ā 
The Ring programming language version 1.6 book - Part 34 of 189
The Ring programming language version 1.6 book - Part 34 of 189The Ring programming language version 1.6 book - Part 34 of 189
The Ring programming language version 1.6 book - Part 34 of 189
Ā 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React Alicante
Ā 

Similar to JavaScript Unit Testing with Evidence Framework

Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testingjeresig
Ā 
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4jeresig
Ā 
Test driven node.js
Test driven node.jsTest driven node.js
Test driven node.jsJay Harris
Ā 
Be smart when testing your Akka code
Be smart when testing your Akka codeBe smart when testing your Akka code
Be smart when testing your Akka codeMykhailo Kotsur
Ā 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptRyan Anklam
Ā 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testingVincent Pradeilles
Ā 
JAVA OOP project; desperately need help asap im begging.Been stuck.pdf
JAVA OOP project; desperately need help asap im begging.Been stuck.pdfJAVA OOP project; desperately need help asap im begging.Been stuck.pdf
JAVA OOP project; desperately need help asap im begging.Been stuck.pdffantasiatheoutofthef
Ā 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unitliminescence
Ā 
(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdf(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdff3apparelsonline
Ā 
JSConf: All You Can Leet
JSConf: All You Can LeetJSConf: All You Can Leet
JSConf: All You Can Leetjohndaviddalton
Ā 
vitest-en.pdf
vitest-en.pdfvitest-en.pdf
vitest-en.pdfssuser65180a
Ā 
package singlylinkedlist; public class Node { public String valu.pdf
package singlylinkedlist; public class Node { public String valu.pdfpackage singlylinkedlist; public class Node { public String valu.pdf
package singlylinkedlist; public class Node { public String valu.pdfamazing2001
Ā 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownpartsBastian Feder
Ā 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitsmueller_sandsmedia
Ā 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good TestsTomek Kaczanowski
Ā 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good TestsTomek Kaczanowski
Ā 
25-functions.ppt
25-functions.ppt25-functions.ppt
25-functions.pptJyothiAmpally
Ā 

Similar to JavaScript Unit Testing with Evidence Framework (20)

Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testing
Ā 
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4
Ā 
JUnit Pioneer
JUnit PioneerJUnit Pioneer
JUnit Pioneer
Ā 
Test driven node.js
Test driven node.jsTest driven node.js
Test driven node.js
Ā 
Be smart when testing your Akka code
Be smart when testing your Akka codeBe smart when testing your Akka code
Be smart when testing your Akka code
Ā 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
Ā 
An introduction to property-based testing
An introduction to property-based testingAn introduction to property-based testing
An introduction to property-based testing
Ā 
JAVA OOP project; desperately need help asap im begging.Been stuck.pdf
JAVA OOP project; desperately need help asap im begging.Been stuck.pdfJAVA OOP project; desperately need help asap im begging.Been stuck.pdf
JAVA OOP project; desperately need help asap im begging.Been stuck.pdf
Ā 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unit
Ā 
(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdf(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdf
Ā 
3 j unit
3 j unit3 j unit
3 j unit
Ā 
JUnit 5
JUnit 5JUnit 5
JUnit 5
Ā 
JSConf: All You Can Leet
JSConf: All You Can LeetJSConf: All You Can Leet
JSConf: All You Can Leet
Ā 
vitest-en.pdf
vitest-en.pdfvitest-en.pdf
vitest-en.pdf
Ā 
package singlylinkedlist; public class Node { public String valu.pdf
package singlylinkedlist; public class Node { public String valu.pdfpackage singlylinkedlist; public class Node { public String valu.pdf
package singlylinkedlist; public class Node { public String valu.pdf
Ā 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
Ā 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
Ā 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
Ā 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
Ā 
25-functions.ppt
25-functions.ppt25-functions.ppt
25-functions.ppt
Ā 

Recently uploaded

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
Ā 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
Ā 
šŸ¬ The future of MySQL is Postgres šŸ˜
šŸ¬  The future of MySQL is Postgres   šŸ˜šŸ¬  The future of MySQL is Postgres   šŸ˜
šŸ¬ The future of MySQL is Postgres šŸ˜RTylerCroy
Ā 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
Ā 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
Ā 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel AraĆŗjo
Ā 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
Ā 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
Ā 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
Ā 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
Ā 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
Ā 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
Ā 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
Ā 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
Ā 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
Ā 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
Ā 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
Ā 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
Ā 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
Ā 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
Ā 

Recently uploaded (20)

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
Ā 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
Ā 
šŸ¬ The future of MySQL is Postgres šŸ˜
šŸ¬  The future of MySQL is Postgres   šŸ˜šŸ¬  The future of MySQL is Postgres   šŸ˜
šŸ¬ The future of MySQL is Postgres šŸ˜
Ā 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
Ā 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
Ā 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Ā 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Ā 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
Ā 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
Ā 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
Ā 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
Ā 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
Ā 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
Ā 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
Ā 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Ā 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
Ā 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
Ā 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
Ā 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
Ā 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Ā 

JavaScript Unit Testing with Evidence Framework

  • 1. Unittesting JavaScript with Evidence Tobie Langel @tobie on twitter
  • 2. ā€œEvidence in its broadest sense includes everything that is used to determine or demonstrate the truth of an assertion.ā€ ā€“Wikipedia
  • 4. Yet Another Unit Test Framework
  • 5. DOH Test.Simple/More JSUnit TestCase QUnit jsUnitTest Crosscheck JSTest J3Unit jsUnity JSNUnit RhinoUnit YUI Test FireUnit JSSpec ... unittest.js JSpec screw-unit
  • 8.
  • 9.
  • 10. A lot of unit tests (> 2000 assertions).
  • 11. A lot of unit tests (> 2000 assertions). Complex cases (async, cross-browser).
  • 12. A lot of unit tests (> 2000 assertions). Complex cases (async, cross-browser). Uses a framework originally created in 2005 for script.aculo.us...
  • 13. A lot of unit tests (> 2000 assertions). Complex cases (async, cross-browser). Uses a framework originally created in 2005 for script.aculo.us... ...with a dependency on Prototype.
  • 16. #2 Environment agnostic (Attempts to run in as many different environments as possible.)
  • 18. #3 Self-contained (Doesnā€™t pollute the global scope.)
  • 23. ā€œI recommend that developers spend 25-50% of their time developing tests.ā€ ā€“Kent Beck
  • 24. Drawing by Witold Riedel (http://www.witoldriedel.com)
  • 25. Kent Beckā€™s original white paper Drawing by Witold Riedel (http://www.witoldriedel.com)
  • 26. Kent Beckā€™s original white paper http://tr.im/kentbeck Drawing by Witold Riedel (http://www.witoldriedel.com)
  • 27. JUnit
  • 33. ... and of course, Kent Beckā€™s original Smalltalk implementation.
  • 34. (
  • 36. !=
  • 37. RSpec
  • 38. BDD
  • 39. Why?
  • 41. &
  • 45. function callAssertions(fn) { var rgxp = /^[^{]*{((.*n*)*)}/m; fn = fn.toString(); fn = fn.match(rgxp)[1]; eval('with (dsl) { with (context) { with (matchers) { ' + fn + ' }}}'); }
  • 46. Function decompilation function callAssertions(fn) { (Not part of any standard.) var rgxp = /^[^{]*{((.*n*)*)}/m; fn = fn.toString(); fn = fn.match(rgxp)[1]; eval('with (dsl) { with (context) { with (matchers) { ' + fn + ' }}}'); }
  • 47. function callAssertions(fn) { var rgxp = /^[^{]*{((.*n*)*)}/m; fn = fn.toString(); fn = fn.match(rgxp)[1]; eval('with (dsl) { with (context) { with (matchers) { ' + fn + ' }}}'); Nested with statements } (Not ES5 strict compliant.)
  • 48. function callAssertions(fn) { var rgxp = /^[^{]*{((.*n*)*)}/m; fn = fn.toString(); eval fn = fn.match(rgxp)[1]; (Not supported in some environments.) eval('with (dsl) { with (context) { with (matchers) { ' + fn + ' }}}'); }
  • 49. function callAssertions(fn) { var rgxp = /^[^{]*{((.*n*)*)}/m; fn = fn.toString(); fn = fn.match(rgxp)[1]; eval('with (dsl) { with (context) { with (matchers) { ' + fn + ' }}}'); }
  • 50. )
  • 51. xUnit
  • 52. xUnit
  • 57. Anatomy of a TestCase
  • 58. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { this.assertEqual('foo', _.ļ¬rst(this.array)); this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } });
  • 59. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, ā€œSubclassā€ TestCase testFirst: function() { this.assertEqual('foo', _.ļ¬rst(this.array)); this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } });
  • 60. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, Give it a name. testFirst: function() { this.assertEqual('foo', _.ļ¬rst(this.array)); this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } });
  • 61. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { Add ļ¬xtures to your _.ļ¬rst(this.array)); this.assertEqual('foo', setUp method. this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } });
  • 62. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { Preļ¬x setUp:testcases {with your function() this.array = ['foo', 'bar', 'baz']; }, test. testFirst: function() { this.assertEqual('foo', _.ļ¬rst(this.array)); this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } });
  • 63. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { this.assertEqual('foo', _.ļ¬rst(this.array)); this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: your assertions. Write function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } });
  • 64. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { this.assertEqual('foo', _.ļ¬rst(this.array)); this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } Optionally add }); meaningful error messages.
  • 66. Evidence.TestCase.extend('AjaxTest', { testAjaxRequest: function(testcase) { testcase.pause(); new Ajax.Request('/some/url', { onComplete: function(response) { testcase.resume(function() { this.assert(response); }); } }); } });
  • 67. Evidence.TestCase.extend('AjaxTest', { testAjaxRequest: function(testcase) { testcase.pause(); new Ajax.Request('/some/url', { instance TestCase onComplete: function(response) { conveniently passed as a testcase.resume(function() { this.assert(response); ļ¬rst argument. }); } }); } });
  • 68. Evidence.TestCase.extend('AjaxTest', { testAjaxRequest: function(testcase) { No need to bind this or testcase.pause(); create your own closure. new Ajax.Request('/some/url', { onComplete: function(response) { testcase.resume(function() { this.assert(response); }); } }); } });
  • 69. Evidence.TestCase.extend('AjaxTest', { testAjaxRequest: function(testcase) { testcase.pause(); new Ajax.Request('/some/url', { onComplete: function(response) { testcase.resume(function() { this.assert(response); }); Bound to the original } }); scope so: } this === testcase });
  • 71. function getTestCaseNames(testcaseClass) { var results = []; for (var property in testcaseClass.prototype) { if (property.indexOf('test') === 0) { results.push(property); } } return results.sort(); }
  • 72. Grab all the methods function getTestCaseNames(testcaseClass) { var results = []; starting with test. for (var property in testcaseClass.prototype) { if (property.indexOf('test') === 0) { results.push(property); } } return results.sort(); }
  • 73. function getTestCaseNames(testcaseClass) { var results = []; for (var property in testcaseClass.prototype) { if (property.indexOf('test') === 0) { results.push(property); } Sort the results. } return results.sort(); }
  • 74. function loadTestsFromTestCase(testcaseClass) { var suite = new TestSuite(testcaseClass.displayName), methodNames = getTestCaseNames(testcaseClass); for (var i = 0; i < methodNames.length; i++) { suite.push(new testcaseClass(methodNames[i])); } return suite; }
  • 75. Create a new suite. function loadTestsFromTestCase(testcaseClass) { var suite = new TestSuite(testcaseClass.displayName), methodNames = getTestCaseNames(testcaseClass); for (var i = 0; i < methodNames.length; i++) { suite.push(new testcaseClass(methodNames[i])); } return suite; }
  • 76. Give it a name. function loadTestsFromTestCase(testcaseClass) { var suite = new TestSuite(testcaseClass.displayName), methodNames = getTestCaseNames(testcaseClass); for (var i = 0; i < methodNames.length; i++) { suite.push(new testcaseClass(methodNames[i])); } return suite; }
  • 77. function loadTestsFromTestCase(testcaseClass) { var suite = new TestSuite(testcaseClass.displayName), methodNames = getTestCaseNames(testcaseClass); for (var i = 0; i < methodNames.length; i++) { suite.push(new testcaseClass(methodNames[i])); } Grab the testcase names. return suite; }
  • 78. function loadTestsFromTestCase(testcaseClass) { var suite = new TestSuite(testcaseClass.displayName), methodNames = getTestCaseNames(testcaseClass); for (var i = 0; i < methodNames.length; i++) { suite.push(new testcaseClass(methodNames[i])); } return suite; Create a new instance of } the testcase class.
  • 79. function loadTestsFromTestCase(testcaseClass) { var suite = new TestSuite(testcaseClass.displayName), methodNames = getTestCaseNames(testcaseClass); for (var i = 0; i < methodNames.length; i++) { suite.push(new testcaseClass(methodNames[i])); } return suite; Add it to your suite. }
  • 82. Evidence.TestCase.extend('ElementTest', { setUp: function() { this.element = document.createElement('div'); }, testElementExtend: function() { var extended = Element.extend(this.element); this.assertEqual(this.element, extended); this.assertRespondsTo('show', extended); // ... }, testElementClone: function() { var cloned = Element.clone(this.element); this.assert(cloned.show); // is extended // ... } });
  • 83. Evidence.TestCase.extend('ElementTest', { setUp: function() { this.element = document.createElement('div'); }, What!? testElementExtend: function() { var extended = Element.extend(this.element); this.assertEqual(this.element, extended); this.assertRespondsTo('show', extended); // ... }, testElementClone: function() { var cloned = Element.clone(this.element); this.assert(cloned.show); // is extended // ... } });
  • 84. Evidence.TestCase.extend('ElementTest', { setUp: function() { this.element = document.createElement('div'); }, testElementExtend: function() { var extended = Element.extend(this.element); this.assertEqual(this.element, extended); this.assertRespondsTo('show', extended); // ... }, Again?! testElementClone: function() { var cloned = Element.clone(this.element); this.assert(cloned.show); // is extended // ... } });
  • 85. Evidence.TestCase.extend('ElementTest', { setUp: function() { this.element = document.createElement('div'); }, testElementExtend: function() { var extended = Element.extend(this.element); this.assertEqual(this.element, extended); this.assertRespondsTo('show', extended); // ... }, testElementClone: function() { var cloned = Element.clone(this.element); this.assert(cloned.show); // is extended // ... OK, now I get it. } });
  • 87. Evidence.TestCase.extend('ElementTest', { //... assertElementExtended: function(element, message) { this._assertExpression( typeof element.show === 'function', message || 'Element is not extended.', 'Expected %o to be extended.', element ); }, testElementExtend: function() { var extended = Element.extend(this.element); this.assertElementExtended(extended); }, testElementClone: function() { var cloned = Element.clone(this.element); this.assertElementExtended(cloned); }
  • 88. Evidence.TestCase.extend('ElementTest', { //... assertElementExtended: function(element, message) { this._assertExpression( typeof element.show === 'function', ! message || 'Element is not extended.', 'Expected %o to be extended.', element ); Syntax still subject to }, change! testElementExtend: function() { var extended = Element.extend(this.element); this.assertElementExtended(extended); }, testElementClone: function() { var cloned = Element.clone(this.element); this.assertElementExtended(cloned); }
  • 90. function getInnerHTML(element) { var html = element.innerHTML.toString(); return html.toLowerCase().gsub(/[rnt]/, ''); } Evidence.TestCase.extend('ElementTest', { // ... testElementInsert: function() { var html = '<p>a paragraph</p>'; Element.insert(this.element, html); this.assertEqual(html, getInnerHTML(this.element)); } });
  • 91. function getInnerHTML(element) { var html = element.innerHTML.toString(); return html.toLowerCase().gsub(/[rnt]/, ''); } Global scope pollution. Evidence.TestCase.extend('ElementTest', { // ... testElementInsert: function() { var html = '<p>a paragraph</p>'; Element.insert(this.element, html); this.assertEqual(html, getInnerHTML(this.element)); } });
  • 92. function getInnerHTML(element) { var html = element.innerHTML.toString(); return html.toLowerCase().gsub(/[rnt]/, ''); } Obscure syntax. Whatā€™s wrong with Evidence.TestCase.extend('ElementTest', { //this.element.innerHTML? ... testElementInsert: function() { var html = '<p>a paragraph</p>'; Element.insert(this.element, html); this.assertEqual(html, getInnerHTML(this.element)); } });
  • 93. Evidence.TestCase.extend('ElementTest', { // ... testElementInsert: function() { var html = '<p>a paragraph</p>'; Element.insert(this.element, html); this.assertEqual(html, this.getElementHTML()); }, getElementHTML: function() { var html = this.element.innerHTML.toString(); return html.toLowerCase().gsub(/[rnt]/, ''); } });
  • 94. Evidence.TestCase.extend('ElementTest', { // ... Instance method! testElementInsert: function() { var html = '<p>a paragraph</p>'; Element.insert(this.element, html); this.assertEqual(html, this.getElementHTML()); }, getElementHTML: function() { var html = this.element.innerHTML.toString(); return html.toLowerCase().gsub(/[rnt]/, ''); } });
  • 95. Evidence.TestCase.extend('ElementTest', { // ... testElementInsert: function() { var html = '<p>a paragraph</p>'; Element.insert(this.element, html); this.assertEqual(html, this.getElementHTML()); }, getElementHTML: function() { Would beneļ¬t from a var html = this.element.innerHTML.toString(); return html.toLowerCase().gsub(/[rnt]/, ''); custom assertion too. } });
  • 97. var suite = loadTestsFromTestCase(ArrayTest); var runner = new Evidence.Runner(); var results = runner.run(suite);
  • 99. Logs to the console. The TestResult
  • 100. Logs to the console. The TestResult Prints to STDOUT.
  • 101. Displays the results in Logs to the console. a web page. The TestResult Prints to STDOUT.
  • 102. Displays the results in Logs to the console. a web page. The TestResult Sends the data back to the server in JSON Prints to STDOUT. (or XML).
  • 104. All of this is good to know,
  • 106. So...
  • 107. Evidence handles it for you!
  • 108. Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { this.assertEqual('foo', _.ļ¬rst(this.array)); this.assertUndeļ¬ned(_.ļ¬rst([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndeļ¬ned(_.last([])); } });
  • 110. ? @tobie on twitter http://github.com/tobie/evidence (Soon hopefully on http://evidencejs.org)