SlideShare a Scribd company logo
1 of 110
Download to read offline
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
Unittesting JavaScript with Evidence
Unittesting JavaScript with Evidence
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', _.first(this.array));
   this.assertUndefined(_.first([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndefined(_.last([]));
  }
});
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },
                              “Subclass” TestCase
 testFirst: function() {
   this.assertEqual('foo', _.first(this.array));
   this.assertUndefined(_.first([]));
 },

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

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

 testFirst: function() {
Add fixtures to your _.first(this.array));
   this.assertEqual('foo',
   setUp method.
   this.assertUndefined(_.first([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndefined(_.last([]));
  }
});
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
Prefix setUp:testcases {with
      your function()
        this.array = ['foo', 'bar', 'baz'];
      },    test.
       testFirst: function() {
         this.assertEqual('foo', _.first(this.array));
         this.assertUndefined(_.first([]));
       },

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

 testFirst: function() {
   this.assertEqual('foo', _.first(this.array));
   this.assertUndefined(_.first([]));
 },

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

 testFirst: function() {
   this.assertEqual('foo', _.first(this.array));
   this.assertUndefined(_.first([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndefined(_.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); first 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.
 }
Benefits
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 benefit 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', _.first(this.array));
   this.assertUndefined(_.first([]));
 },

  testLast: function() {
    this.assertEqual('bar', _.last(this.array),
     'Failed to grab the last element of the array.');
    this.assertUndefined(_.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 Unittesting JavaScript with Evidence

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
 
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
 
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
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownpartsBastian Feder
 
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
 
----------Evaluator-java---------------- package evaluator- import j.docx
----------Evaluator-java---------------- package evaluator-   import j.docx----------Evaluator-java---------------- package evaluator-   import j.docx
----------Evaluator-java---------------- package evaluator- import j.docxAdamq0DJonese
 
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docxsrcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docxwhitneyleman54422
 

Similar to Unittesting JavaScript with Evidence (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
 
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
 
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
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
 
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
 
----------Evaluator-java---------------- package evaluator- import j.docx
----------Evaluator-java---------------- package evaluator-   import j.docx----------Evaluator-java---------------- package evaluator-   import j.docx
----------Evaluator-java---------------- package evaluator- import j.docx
 
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docxsrcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
srcArtifact.javasrcArtifact.javaclassArtifactextendsCave{pub.docx
 

Recently uploaded

AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesMd Hossain Ali
 
Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Adtran
 
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsIgniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsSafe Software
 
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"DianaGray10
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostMatt Ray
 
UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5DianaGray10
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaborationbruanjhuli
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Websitedgelyza
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024D Cloud Solutions
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxGDSC PJATK
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfinfogdgmi
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfAijun Zhang
 
Comparing Sidecar-less Service Mesh from Cilium and Istio
Comparing Sidecar-less Service Mesh from Cilium and IstioComparing Sidecar-less Service Mesh from Cilium and Istio
Comparing Sidecar-less Service Mesh from Cilium and IstioChristian Posta
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfDianaGray10
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8DianaGray10
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1DianaGray10
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationIES VE
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfJamie (Taka) Wang
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdfPedro Manuel
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7DianaGray10
 

Recently uploaded (20)

AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
 
Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™
 
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration WorkflowsIgniting Next Level Productivity with AI-Infused Data Integration Workflows
Igniting Next Level Productivity with AI-Infused Data Integration Workflows
 
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
 
UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5UiPath Studio Web workshop series - Day 5
UiPath Studio Web workshop series - Day 5
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
 
COMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a WebsiteCOMPUTER 10 Lesson 8 - Building a Website
COMPUTER 10 Lesson 8 - Building a Website
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptx
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdf
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdf
 
Comparing Sidecar-less Service Mesh from Cilium and Istio
Comparing Sidecar-less Service Mesh from Cilium and IstioComparing Sidecar-less Service Mesh from Cilium and Istio
Comparing Sidecar-less Service Mesh from Cilium and Istio
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1
 
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve DecarbonizationUsing IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
Using IESVE for Loads, Sizing and Heat Pump Modeling to Achieve Decarbonization
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdf
 
UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7UiPath Studio Web workshop series - Day 7
UiPath Studio Web workshop series - Day 7
 

Unittesting JavaScript with Evidence

  • 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
  • 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. &
  • 42. JavaScript’s awful at writing DSLs
  • 44. you’re using it’s bad parts!
  • 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', _.first(this.array)); this.assertUndefined(_.first([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.last([])); } });
  • 59. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, “Subclass” TestCase testFirst: function() { this.assertEqual('foo', _.first(this.array)); this.assertUndefined(_.first([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.last([])); } });
  • 60. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, Give it a name. testFirst: function() { this.assertEqual('foo', _.first(this.array)); this.assertUndefined(_.first([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.last([])); } });
  • 61. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { Add fixtures to your _.first(this.array)); this.assertEqual('foo', setUp method. this.assertUndefined(_.first([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.last([])); } });
  • 62. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { Prefix setUp:testcases {with your function() this.array = ['foo', 'bar', 'baz']; }, test. testFirst: function() { this.assertEqual('foo', _.first(this.array)); this.assertUndefined(_.first([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.last([])); } });
  • 63. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { this.assertEqual('foo', _.first(this.array)); this.assertUndefined(_.first([])); }, testLast: your assertions. Write function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.last([])); } });
  • 64. var ArrayTest = Evidence.TestCase.extend('ArrayTest', { setUp: function() { this.array = ['foo', 'bar', 'baz']; }, testFirst: function() { this.assertEqual('foo', _.first(this.array)); this.assertUndefined(_.first([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.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); first 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 benefit 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', _.first(this.array)); this.assertUndefined(_.first([])); }, testLast: function() { this.assertEqual('bar', _.last(this.array), 'Failed to grab the last element of the array.'); this.assertUndefined(_.last([])); } });
  • 109. It’s all you need!
  • 110. ? @tobie on twitter http://github.com/tobie/evidence (Soon hopefully on http://evidencejs.org)