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

Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 

Recently uploaded (20)

Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
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
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 

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)