<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://devlicio.us/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Sergio Pereira : UnitTesting, JavaScript</title><link>http://devlicio.us/blogs/sergio_pereira/archive/tags/UnitTesting/JavaScript/default.aspx</link><description>Tags: UnitTesting, JavaScript</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 SP1 (Build: 31106.3070)</generator><item><title>Test-Driving a new feature for JavaScript</title><link>http://devlicio.us/blogs/sergio_pereira/archive/2009/11/12/test-driving-a-new-feature-for-javascript.aspx</link><pubDate>Fri, 13 Nov 2009 01:16:00 GMT</pubDate><guid isPermaLink="false">40756a8b-6212-4073-9d98-6c26781577de:53698</guid><dc:creator>sergiopereira</dc:creator><slash:comments>5</slash:comments><description>&lt;/p&gt;
Earlier this week I had a piece of JavaScript that I wanted to like this:
&lt;p&gt;

&lt;pre name="code" class="js:nogutter"&gt;var index = someArray.indexOf(someObject);&lt;/pre&gt;

&lt;p&gt;
The problem there is that the &lt;code&gt;indexOf&lt;/code&gt; method of
the &lt;code&gt;Array&lt;/code&gt; object was introduced in JavaScript 1.6,
which &lt;a href="http://ejohn.org/blog/versions-of-javascript/"&gt;isn&amp;#39;t implemented in all browsers&lt;/a&gt;
(actually, IE seems to be the real problem here).
&lt;/p&gt;
&lt;p&gt;
Anyway, thanks to the awesomeness of dynamic typing and
&lt;a href="http://devlicio.us/blogs/sergio_pereira/archive/2009/06/12/javascript-not-your-father-s-inheritance-model-part-2.aspx"&gt;prototypeal inheritance&lt;/a&gt;
in JavaScript, we can fix that ourselves with code similar to the below. 
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(item) {
    // implementation goes here
  };
}&lt;/pre&gt;

&lt;p&gt;&lt;i&gt;Actually, if you&amp;#39;re just looking for the final solution you can
find it easily with your search engine of choice or skip to the 
&lt;a&gt;bottom of this article&lt;/a&gt;.&lt;/i&gt;&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;
But, instead of going through the normal trial and error approach,
I chose to flex my TDD muscle and try to create this method using
a test-first routine. For this exercise, I chose 
&lt;a href="http://docs.jquery.com/QUnit"&gt;QUnit&lt;/a&gt; as the unit testing framework.
&lt;/p&gt;
&lt;p&gt;
I started with a standard empty testing HTML file where I&amp;#39;ll put my test cases and
a reference to my-library.js where I&amp;#39;ll add this new method. 
&lt;/p&gt;

&lt;pre name="code" class="html:nogutter"&gt;&amp;lt;html&amp;amp;gt
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Tests for mylibrary.js&amp;lt;/title&amp;gt;
    &amp;lt;link href=&amp;quot;testsuite.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;
    &amp;lt;script src=&amp;quot;jquery.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;testrunner.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;my-library.js&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Tests for mylibrary.js&amp;lt;/h1&amp;gt;
    &amp;lt;h2 id=&amp;quot;banner&amp;quot;&amp;gt;
      &amp;lt;span style=&amp;quot;color:#fff;&amp;quot;&amp;gt;Result: Red/Green?&amp;lt;/span&amp;gt;
    &amp;lt;/h2&amp;gt;
    &amp;lt;h2 id=&amp;quot;userAgent&amp;quot;&amp;gt;&amp;lt;/h2&amp;gt;
    &amp;lt;ol id=&amp;quot;tests&amp;quot;&amp;gt;&amp;lt;/ol&amp;gt;
     &amp;lt;!--
        The #main element is like your test fixture. It&amp;#39;s like the 
		data for your tests.     
        Whatever exists inside the #main element gets restored 
        before each test.
        Feel free to manipulate the contents of #main in your tests.
      --&amp;gt;
    &amp;lt;div id=&amp;quot;main&amp;quot;&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;

&lt;p&gt;
You can put your test cases in a separate .js file and reference it here but
I&amp;#39;ll just add the necessary JavaScript to the HTML file itself in this exercise.
&lt;/p&gt;
&lt;p&gt;
Here are the test cases I&amp;#39;m going to support:
&lt;/p&gt;

&lt;ol&gt;
	&lt;li&gt;Missing items return -1&lt;/li&gt;
	&lt;li&gt;Existing item is found&lt;/li&gt;
	&lt;li&gt;Existing item is found with positive starting index&lt;/li&gt;
	&lt;li&gt;Existing item is not found with positive starting index&lt;/li&gt;
	&lt;li&gt;Existing item is found with negative starting index&lt;/li&gt;
	&lt;li&gt;Existing item is not found with negative starting index&lt;/li&gt;
	&lt;li&gt;Comparison is non-coercive&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Let&amp;#39;s start with the first test: &lt;i&gt;Missing items return -1&lt;/i&gt;. It&amp;#39;s a simple value comparison.
At this point I do not have anything written yet for the &lt;code&gt;indexOf&lt;/code&gt; function,
it doesn&amp;#39;t even exist if you&amp;#39;re on IE. Add the following to the HTML file.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;$(function() {
  var list = [11, 22, 33, 44];
  
  module(&amp;#39;Array.indexOf&amp;#39;);
  
  test(&amp;#39;Missing items return -1&amp;#39;, function() {
    equals(list.indexOf(1234), -1);
  });

});&lt;/pre&gt;                

&lt;p&gt;
When you open the HTML file on IE (I&amp;#39;m using IE8), you&amp;#39;ll see this:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_1.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Totally expected, but let&amp;#39;s try to make this test pass. In the my-library.js file,
let&amp;#39;s add the simplest code that can satisfy that test.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function() {
      return -1;
    };
}&lt;/pre&gt;

&lt;p&gt;
The outer &lt;code&gt;if&lt;/code&gt; is there just to prevent replacing the function if
it already exists. The function is clearly incomplete, but that&amp;#39;s not the point.
The point is that it passes the test:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_2.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Great. Time for the second test: &lt;i&gt;Existing item is found&lt;/i&gt;. For that one
we will need to loop through the items in the array. The array is &lt;code&gt;this&lt;/code&gt;
in the method. Here&amp;#39;s the test, which we add right after the first one.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;test(&amp;#39;Existing item is found&amp;#39;, function() {
  equals(list.indexOf(22), 1);
});&lt;/pre&gt;

&lt;p&gt;And of course that fails&lt;/p&gt;

&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_3.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Time to make it pass.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(item) {
    for (var i=0; i&amp;lt;this.length; i++) {
      if (this[i] == item) {
        return i;
      }
    }
    return -1;
  };
}&lt;/pre&gt;
		
&lt;p&gt;
And it passes:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_4.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Now let&amp;#39;s tackle the third test case: &lt;i&gt;Existing item is found with positive starting index&lt;/i&gt;.
This one starts to make things interesting. Here&amp;#39;s the test.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;test(&amp;#39;Existing item is found with positive starting index&amp;#39;, function() {
  //let&amp;#39;s try a few boundary conditions
  equals(list.indexOf(33, 0), 2);
  equals(list.indexOf(33, 1), 2);
  equals(list.indexOf(33, 2), 2);
});&lt;/pre&gt;

&lt;p&gt;
Hmmm. The test passes, as we can see below. Well, that was kind of an accident but 
I&amp;#39;ll leave the test as is because it does test what was specified. Leaving the
test will help us if we make changes that break this specification.
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_5.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
The next test case is &lt;i&gt;Existing item is not found with positive starting index&lt;/i&gt;. We could 
write it like this:
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;test(&amp;#39;Existing item is not found with positive starting index&amp;#39;, function() {
  equals(list.indexOf(33, 3), -1);
  equals(list.indexOf(33, 1000), -1);
});&lt;/pre&gt;

&lt;p&gt;
And boom! It fails. It should, we don&amp;#39;t even have support for that second parameter yet.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_6.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Failing it is, pass it we must. First attempt:
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(item, startIndex) {
    for (var i=startIndex; i&amp;lt;this.length; i++) {
      if (this[i] == item) {
        return i;
      }
    }
    return -1;
  };
}&lt;/pre&gt;

&lt;p&gt;
It passes the new test, but it fails another one:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_7.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
That&amp;#39;s actually pretty cool. We would have created a mess if we had just
added the second parameter without some regression testing. Hooray for
unit tests! 
&lt;/p&gt;
&lt;p&gt;
What&amp;#39;s happening is that my &lt;code&gt;startIndex&lt;/code&gt; defaults to &lt;code&gt;undefined&lt;/code&gt;
if not passed by the caller. It should default to zero. Easy fix.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(item, startIndex) {
    startIndex = startIndex || 0;
    for (var i=startIndex; i&amp;lt;this.length; i++) {
      if (this[i] == item) {
        return i;
      }
    }
    return -1;
  };
}&lt;/pre&gt;	

&lt;p&gt;
I see the green light!
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_8.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Our next test case is up: &lt;i&gt;Existing item is found with negative starting index&lt;/i&gt;.
What we are trying to do here is allow the caller to specify the starting position as
an offset from the last item using a negative number. So in a 4-element array, passing
-1 means that we start at the last item and passing -3 we start at the second item. See 
the test.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;test(&amp;#39;Existing item is found with negative starting index&amp;#39;, function() {
  equals(list.indexOf(33, -2), 2);
  equals(list.indexOf(33, -3), 2);
  equals(list.indexOf(33, -1000), 2);
});&lt;/pre&gt;

&lt;p&gt;
Once again it passes by accident. 
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_9.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
The next text case will show that it was an accident:
&lt;i&gt;Existing item is not found with negative starting index&lt;/i&gt;. The test for that is below:
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;test(&amp;#39;Existing item is not found with negative starting index&amp;#39;, function() {
  equals(list.indexOf(33, -1), -1);
  equals(list.indexOf(11, -3), -1);
});&lt;/pre&gt;


&lt;p&gt;
It fails:
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_10.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Making it pass is not that hard, we just need to compute a positive 
version of that negative start index.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(item, startIndex) {
    startIndex = startIndex || 0;
    
    if (startIndex &amp;lt; 0) {
      startIndex += this.length;
    }
    
    for (var i=startIndex; i&amp;lt;this.length; i++) {
      if (this[i] == item) {
        return i;
      }
    }
    return -1;
  };
}&lt;/pre&gt;	

&lt;p&gt;	
Ding, ding, ding! Another passing test for our team.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_11.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
Lastly, we need to make sure that the &lt;i&gt;Comparison is non-coercive&lt;/i&gt;. By
that we mean, a number will never equal a string &amp;mdash; or generally speaking, the
things being compared must be of the same type. Let&amp;#39;s get some tests that
try to point out tha flaw in our current code.
&lt;/p&gt;

&lt;pre name="code" class="js:nogutter"&gt;test(&amp;#39;Comparison is non-coercive&amp;#39;, function() {
  equals(list.indexOf(&amp;#39;22&amp;#39;), -1);
});&lt;/pre&gt;

&lt;p&gt;
This fails because, strictly speaking, &lt;code&gt;22&lt;/code&gt; and &lt;code&gt;&amp;#39;22&amp;#39;&lt;/code&gt;
are two different things and we didn&amp;#39;t want to match that element like we did.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_12.png" alt="" /&gt;
&lt;/p&gt;

&lt;p&gt;
	On to the fix. Can you spot the difference? Try harder.
&lt;/p&gt;
&lt;a name="solution"&gt;&lt;/a&gt;
&lt;pre name="code" class="js:nogutter"&gt;if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(item, startIndex) {
    startIndex = startIndex || 0;
    
    if (startIndex &amp;lt; 0) {
        startIndex += this.length;
    }
    
    for (var i=startIndex; i&amp;lt;this.length; i++) {
        if (this[i] === item) {
          return i;
        }
    }
    return -1;
  };
}&lt;/pre&gt;	

&lt;p&gt;
And here is the QUnit report in all its successful glory.
&lt;/p&gt;
&lt;p&gt;
&lt;img src="http://devlicio.us/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/sergio_5F00_pereira.2009.11/tdd_2D00_qunit_2D00_13.png" alt="" /&gt;
&lt;/p&gt;
&lt;p&gt;
If you&amp;#39;ve never been exposed to TDD this may have felt awkward but
hopefully you noticed that it&amp;#39;s more of an evolutionary design
approach, as opposed to trying to make it perfect on the first attempt or
even reckless patching.
&lt;/p&gt;
&lt;p&gt;
It also left us with a valuable collection of tests that we
can execute after making changes to the code, ensuring we didn&amp;#39;t
break anything by accident.
&lt;p&gt;	

&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://devlicio.us/aggbug.aspx?PostID=53698" width="1" height="1"&gt;</description><category domain="http://devlicio.us/blogs/sergio_pereira/archive/tags/Development/default.aspx">Development</category><category domain="http://devlicio.us/blogs/sergio_pereira/archive/tags/JavaScript/default.aspx">JavaScript</category><category domain="http://devlicio.us/blogs/sergio_pereira/archive/tags/UnitTesting/default.aspx">UnitTesting</category></item></channel></rss>