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.”
 ...
http://github.com/tobie/evidence
Yet Another Unit Test
     Framework
DOH           Test.Simple/More
JSUnit        TestCase
QUnit         jsUnitTest
Crosscheck    JSTest
J3Unit        jsUnity
...
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...
A lot of unit tests (> 2000 assertions).
Complex cases (async, cross-browser).
Uses a framework originally created in
2005...
#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 (htt...
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('w...
Function decompilation
function callAssertions(fn) {
                      (Not part of any standard.)
 var rgxp = /^[^{]*...
function callAssertions(fn) {
 var rgxp = /^[^{]*{((.*n*)*)}/m;
 fn = fn.toString();
 fn = fn.match(rgxp)[1];

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

    eval('w...
)
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'];
 },

 ...
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },
  ...
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },
  ...
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 ...
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
Prefix setUp:testcases {with
      your function()
        this.arr...
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 ...
var ArrayTest = Evidence.TestCase.extend('ArrayTest', {
 setUp: function() {
   this.array = ['foo', 'bar', 'baz'];
 },

 ...
Built-in async handling.
Evidence.TestCase.extend('AjaxTest', {
  testAjaxRequest: function(testcase) {
    testcase.pause();
    new Ajax.Request(...
Evidence.TestCase.extend('AjaxTest', {
  testAjaxRequest: function(testcase) {
    testcase.pause();
    new Ajax.Request(...
Evidence.TestCase.extend('AjaxTest', {
    testAjaxRequest: function(testcase) {
No need to bind this or
      testcase.pa...
Evidence.TestCase.extend('AjaxTest', {
  testAjaxRequest: function(testcase) {
    testcase.pause();
    new Ajax.Request(...
The TestSuite
function getTestCaseNames(testcaseClass) {
 var results = [];

    for (var property in testcaseClass.prototype) {
      i...
Grab all the methods
function getTestCaseNames(testcaseClass) {
 var results = [];        starting with test.

    for (va...
function getTestCaseNames(testcaseClass) {
 var results = [];

    for (var property in testcaseClass.prototype) {
      i...
function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = g...
Create a new suite.

function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName)...
Give it a name.

function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
  ...
function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = g...
function loadTestsFromTestCase(testcaseClass) {
 var suite = new TestSuite(testcaseClass.displayName),
    methodNames = g...
function loadTestsFromTestCase(testcaseClass) {
  var suite = new TestSuite(testcaseClass.displayName),
     methodNames =...
Benefits
Custom assertions
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },

 test...
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },
      ...
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },

 test...
Evidence.TestCase.extend('ElementTest', {
 setUp: function() {
   this.element = document.createElement('div');
 },

 test...
!= DRY
Evidence.TestCase.extend('ElementTest', {
 //...

 assertElementExtended: function(element, message) {
   this._assertExpr...
Evidence.TestCase.extend('ElementTest', {
 //...

 assertElementExtended: function(element, message) {
   this._assertExpr...
Instance methods
function getInnerHTML(element) {
  var html = element.innerHTML.toString();
  return html.toLowerCase().gsub(/[rnt]/, '');...
function getInnerHTML(element) {
  var html = element.innerHTML.toString();
  return html.toLowerCase().gsub(/[rnt]/, '');...
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>';
  ...
Evidence.TestCase.extend('ElementTest', {
 // ...
              Instance method!
 testElementInsert: function() {
   var h...
Evidence.TestCase.extend('ElementTest', {
 // ...

 testElementInsert: function() {
   var html = '<p>a paragraph</p>';
  ...
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

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



               The TestResult
    Sends the data back
...
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: funct...
It’s all you need!
?
           @tobie on twitter

http://github.com/tobie/evidence
 (Soon hopefully on http://evidencejs.org)
Unittesting JavaScript with Evidence
Unittesting JavaScript with Evidence
Upcoming SlideShare
Loading in...5
×

Unittesting JavaScript with Evidence

6,935

Published on

Evidence is a new, framework-agnostic unit testing library which I developed out of necessity and frustration with the existing offering. Although it's heavily inspired by it's Ruby, Python and Java couterparts, Evidence is packed with niceness targeted at the specificities of the JavaScript language and its different environments. Hopefully this introduction to Evidence will give you the motivation, tools and knowledge to start unit testing your JavaScript code if you are not doing so already.

Published in: Technology, Health & Medicine
1 Comment
18 Likes
Statistics
Notes
  • Javascript grows up :->
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
6,935
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
126
Comments
1
Likes
18
Embeds 0
No embeds

No notes for slide

Unittesting JavaScript with Evidence

  1. 1. Unittesting JavaScript with Evidence Tobie Langel @tobie on twitter
  2. 2. “Evidence in its broadest sense includes everything that is used to determine or demonstrate the truth of an assertion.” –Wikipedia
  3. 3. http://github.com/tobie/evidence
  4. 4. Yet Another Unit Test Framework
  5. 5. DOH Test.Simple/More JSUnit TestCase QUnit jsUnitTest Crosscheck JSTest J3Unit jsUnity JSNUnit RhinoUnit YUI Test FireUnit JSSpec ... unittest.js JSpec screw-unit
  6. 6. Why?
  7. 7. Scratching our own itch
  8. 8. A lot of unit tests (> 2000 assertions).
  9. 9. A lot of unit tests (> 2000 assertions). Complex cases (async, cross-browser).
  10. 10. A lot of unit tests (> 2000 assertions). Complex cases (async, cross-browser). Uses a framework originally created in 2005 for script.aculo.us...
  11. 11. 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.
  12. 12. #1 Framework agnostic
  13. 13. #2 Environment agnostic
  14. 14. #2 Environment agnostic (Attempts to run in as many different environments as possible.)
  15. 15. #3 Self-contained
  16. 16. #3 Self-contained (Doesn’t pollute the global scope.)
  17. 17. #4 Reliable
  18. 18. #5 Built with async in mind
  19. 19. #6 Easier to automate
  20. 20. #7 Promotes better testing
  21. 21. “I recommend that developers spend 25-50% of their time developing tests.” –Kent Beck
  22. 22. Drawing by Witold Riedel (http://www.witoldriedel.com)
  23. 23. Kent Beck’s original white paper Drawing by Witold Riedel (http://www.witoldriedel.com)
  24. 24. Kent Beck’s original white paper http://tr.im/kentbeck Drawing by Witold Riedel (http://www.witoldriedel.com)
  25. 25. JUnit
  26. 26. JUnit Kent Beck & Erich Gamma
  27. 27. Test::Unit
  28. 28. MiniTest
  29. 29. unittest / PyUnit
  30. 30. YUI.Test
  31. 31. ... and of course, Kent Beck’s original Smalltalk implementation.
  32. 32. (
  33. 33. Evidence
  34. 34. !=
  35. 35. RSpec
  36. 36. BDD
  37. 37. Why?
  38. 38. I don’t like BDD
  39. 39. &
  40. 40. JavaScript’s awful at writing DSLs
  41. 41. unless
  42. 42. you’re using it’s bad parts!
  43. 43. function callAssertions(fn) { var rgxp = /^[^{]*{((.*n*)*)}/m; fn = fn.toString(); fn = fn.match(rgxp)[1]; eval('with (dsl) { with (context) { with (matchers) { ' + fn + ' }}}'); }
  44. 44. 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 + ' }}}'); }
  45. 45. 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.)
  46. 46. 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 + ' }}}'); }
  47. 47. function callAssertions(fn) { var rgxp = /^[^{]*{((.*n*)*)}/m; fn = fn.toString(); fn = fn.match(rgxp)[1]; eval('with (dsl) { with (context) { with (matchers) { ' + fn + ' }}}'); }
  48. 48. )
  49. 49. xUnit
  50. 50. xUnit
  51. 51. xUnit TestCase
  52. 52. xUnit TestCase TestSuite
  53. 53. xUnit TestCase TestSuite TestRunner
  54. 54. xUnit TestCase TestSuite TestRunner TestResult
  55. 55. Anatomy of a TestCase
  56. 56. 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([])); } });
  57. 57. 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([])); } });
  58. 58. 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([])); } });
  59. 59. 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([])); } });
  60. 60. 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([])); } });
  61. 61. 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([])); } });
  62. 62. 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.
  63. 63. Built-in async handling.
  64. 64. Evidence.TestCase.extend('AjaxTest', { testAjaxRequest: function(testcase) { testcase.pause(); new Ajax.Request('/some/url', { onComplete: function(response) { testcase.resume(function() { this.assert(response); }); } }); } });
  65. 65. 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. }); } }); } });
  66. 66. 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); }); } }); } });
  67. 67. 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 });
  68. 68. The TestSuite
  69. 69. function getTestCaseNames(testcaseClass) { var results = []; for (var property in testcaseClass.prototype) { if (property.indexOf('test') === 0) { results.push(property); } } return results.sort(); }
  70. 70. 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(); }
  71. 71. 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(); }
  72. 72. 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; }
  73. 73. 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; }
  74. 74. 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; }
  75. 75. 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; }
  76. 76. 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.
  77. 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])); } return suite; Add it to your suite. }
  78. 78. Benefits
  79. 79. Custom assertions
  80. 80. 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 // ... } });
  81. 81. 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 // ... } });
  82. 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); // ... }, Again?! testElementClone: function() { var cloned = Element.clone(this.element); this.assert(cloned.show); // is extended // ... } });
  83. 83. 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. } });
  84. 84. != DRY
  85. 85. 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); }
  86. 86. 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); }
  87. 87. Instance methods
  88. 88. 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)); } });
  89. 89. 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)); } });
  90. 90. 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)); } });
  91. 91. 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]/, ''); } });
  92. 92. 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]/, ''); } });
  93. 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() { Would benefit from a var html = this.element.innerHTML.toString(); return html.toLowerCase().gsub(/[rnt]/, ''); custom assertion too. } });
  94. 94. The TestRunner
  95. 95. var suite = loadTestsFromTestCase(ArrayTest); var runner = new Evidence.Runner(); var results = runner.run(suite);
  96. 96. The TestResult
  97. 97. Logs to the console. The TestResult
  98. 98. Logs to the console. The TestResult Prints to STDOUT.
  99. 99. Displays the results in Logs to the console. a web page. The TestResult Prints to STDOUT.
  100. 100. 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).
  101. 101. The Magic
  102. 102. All of this is good to know,
  103. 103. but it’s tedious.
  104. 104. So...
  105. 105. Evidence handles it for you!
  106. 106. 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([])); } });
  107. 107. It’s all you need!
  108. 108. ? @tobie on twitter http://github.com/tobie/evidence (Soon hopefully on http://evidencejs.org)
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×