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', _.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 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
 
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
 

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

Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
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
 
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
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Hyundai Motor Group
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetEnjoy Anytime
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 

Recently uploaded (20)

Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
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
 
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
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your BudgetHyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
Hyderabad Call Girls Khairatabad ✨ 7001305949 ✨ Cheap Price Your Budget
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 

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. &
  • 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)