Tuesday, April 07, 2009

JavaScript Unit Testing Part 1: JsUnit

In this post we will examine the 1st of 4 JavaScript Unit Testing frameworks... JsUnit.

In subsequent posts we will cover JSSpec, QUnit, and YUI Test.

First of all, lets define some JavaScript code that we want to Unit Test. I wrote the following Pig Latin JavaScript utility for the sole purpose of this Unit Testing series…

*Note: I am aware the code needs to be refactored. We will address that in a future blog post :) 

/*const*/ var CONSONANTS = 'bcdfghjklmnpqrstvwxyz';
/*const*/ var VOWELS = 'aeiou';

function EnglishToPigLatin(english) {
   /*const*/ var SYLLABLE = 'ay';

   var pigLatin = '';
      
   if (english != null && english.length > 0 && 
      (VOWELS.indexOf(english[0]) > -1 || CONSONANTS.indexOf(english[0]) > -1 )) {
      if (VOWELS.indexOf(english[0]) > -1) {
         pigLatin = english + SYLLABLE;
      } else {      
         var preConsonants = '';
         for (var i = 0; i < english.length; ++i) {
            if (CONSONANTS.indexOf(english[i]) > -1) {
               preConsonants += english[i];
               if (preConsonants == 'q' && i+1 < english.length && english[i+1] == 'u') {
                  preConsonants += 'u';
                  i += 2;
                  break;
               }
            } else {
               break;
            }
         }
         pigLatin = english.substring(i) + preConsonants + SYLLABLE;
      }
   }
   
   return pigLatin;
}

Next we need to define a set of Unit Tests using JsUnit. I am using JsUnit 2.2 Alpha 11 for this example.

In order to create the Unit Tests we need to know the business rules for the Pig Latin method…

  1. In words that begin with consonant sounds, the initial consonant or consonant cluster or diagraph is moved to the end of the word, and "ay" is added…
  2. In words that begin with vowel sounds or silent consonants, the syllable "ay" is added to the end of the word. In some dialects, to aid in pronunciation, an extra consonant is added to the beginning of the suffix.

Now that we know the rules, lets start making some Unit Tests…

<html>
 <head>
   <title>Test Page for EnglishToPigLatin(english)</title>
   <link rel="stylesheet" type="text/css" href="../css/jsUnitStyle.css">
   <script language="JavaScript" type="text/javascript" src="../app/jsUnitCore.js"></script>
   <script language="JavaScript" type="text/javascript" src="./PigLatin.js"></script>
 </head>
 <body>
  <script language="javascript">
   function testPassingNullShouldReturnBlank() {
      assertEquals('null returns a blank', '', EnglishToPigLatin(null));
   }
    
   function testPassingBlankShouldReturnBlank() {
      assertEquals('blank returns a blank', '', EnglishToPigLatin(''));
   }
  
   function testConsonantBeastShouldReturnEastbay() {
      assertEquals('beast returns eastbay', 'eastbay', EnglishToPigLatin('beast'));
   }   
  
   function testConsonantDoughShouldreturnOughday() {
      assertEquals("dough returns oughday", 'oughday', EnglishToPigLatin('dough'));
   }
  
   function testConsonantHappyShouldReturnAppyhay() {
      assertEquals("happy returns appyhay", 'appyhay', EnglishToPigLatin('happy'));
   }        

   function testConsonantsQuestionShouldReturnEstionquay() {
      assertEquals("question returns estionquay", 'estionquay', EnglishToPigLatin('question'));
   }
    
   function testConsonantsStarShouldReturnArstay() {
      assertEquals("star returns arstay", 'arstay', EnglishToPigLatin('star'));
   } 
    
   function testConsonantsThreeShouldReturnEethray() {
      assertEquals("three returns eethray", 'eethray', EnglishToPigLatin('three'));
   }

   function testVowelAppleShouldReturnAppleay() {
      assertEquals("apple returns appleay", 'appleay', EnglishToPigLatin('apple'));
   }
    
   function testVowelElijahShouldReturnElijahay() {
      assertEquals("elijah returns elijahay", 'elijahay', EnglishToPigLatin('elijah'));
   }
    
   function testVowelIglooShouldReturnIglooay() {
      assertEquals("igloo returns iglooay", 'iglooay', EnglishToPigLatin('igloo'));
   }
    
   function testVowelOctopusShouldReturnOctopusay() {
      assertEquals("octopus returns octopusay", 'octopusay', EnglishToPigLatin('octopus'));
   }
    
   function testVowelUmbrellaShouldReturnUmbrellaay() {
      assertEquals("umbrella returns umbrellaay", 'umbrellaay', EnglishToPigLatin('umbrella'));
   }
    
   function testPassingNumberShouldReturnBlank() {
      assertEquals("1234567890 returns blank", '', EnglishToPigLatin('1234567890'));
   }

   function testPassingSymbolsShouldReturnBlank() {
      assertEquals("~!@#$%^&*()_+ returns blank", '', EnglishToPigLatin('~!@#$%^&*()_+'));
   }
    </script>
 </body>
</html>

There are several different types of asserts that JsUnit supports...

  1. assert([comment], booleanValue)
  2. assertTrue([comment], booleanValue)
  3. assertFalse([comment], booleanValue)
  4. assertEquals([comment], value1, value2)
  5. assertNotEquals([comment], value1, value2)
  6. assertNull([comment], value)
  7. assertNotNull([comment], value)
  8. assertUndefined([comment], value)
  9. assertNotUndefined([comment], value)
  10. assertNaN([comment], value)
  11. assertNotNaN([comment], value)
  12. fail(comment)

JsUnit has a testRunner to execute all of your Unit Tests. If all is well, your status bar will be green. If any asserts failed, they will be displayed in a list control where any assert comment can be retrieved

testRunner

Overall, JsUnit is a decent Unit Testing framework for JavaScript. You can also bundle tests into suites and run multiple suites. As most testing frameworks, you can also use setUp() and tearDown() methods to prepare the environment for each test method.

2 comments:

  1. Anonymous7:30 AM

    Unfortunately, JsUnit does not work in Firefox 3 and it has not seen any releases since March 2006.

    ReplyDelete
  2. I'm curious that you choose to write code first and tests later. In my experience, using more of a TDD tests-first approach has been very helpful in driving the generation of clean code.

    ReplyDelete