2.3. Testing Cookbook  page     

Testing Cookbook

FuncUnit tiered approach allows unit and functional testing in the browser, Rhino, and Selenium. When you scaffolded recipe, it created tests for you. This guide will show you how to:

  • Run unit tests.
  • Run functional tests.
  • Understand the qUnit unit tests.
  • Understand the FuncUnit functional tests.
  • Test isTasty functionality.

Run Unit Tests

JavaScriptMVC uses qUnit to test unit functionality (like models and basic plugins). You can run these tests in the browser or Envjs.

cookbook/test/qunit/qunit.js loads qunit and your unit tests. Make sure you have added recipe_test.js like:

steal
  .plugins("funcunit/qunit", "cookbook")
  .then("cookbook_test",'recipe_test')

Run Unit Tests in the Browser

Open cookbook/qunit.html. You should see something like:

Run Unit Tests in Envjs

In a command window type:

> funcunit\envjs cookbook/qunit.html
This runs qunit.html in a simulated browser environment. The output should look like:

Run Functional Tests

JavaScriptMVC uses FuncUnit to add browser and selenium-based functional testing to qUnit. You can run tests in the browser or using selenium.

cookbook/test/funcunit/funcunit.js loads funcunit and your functional tests. Make sure you have added recipe_controller_test.js like:

steal
 .plugins("funcunit")
 .then("cookbook_test",'recipe_controller_test')

Run Functional Tests in the Browser

Open cookbook/funcunit.html. You should see something like:

Run Functional Tests in Selenium

In a command window type:

> funcunit\envjs cookbook\funcunit.html
This should open Firefox and IE if you are using Windows. The results of the test should look like:
If Selenium is unable to open your browsers, it's likely you have them in an unusual location. Read FuncUnit.browsers for information on how to configure browsers so selenium can find them.

If you are having trouble running the tests in Internet Explorer, you need to change a few settings in the browser. Please see the FuncUnit documentation for troubleshooting help.

Understanding qUnit Tests

FuncUnit adds very little to qUnit, so the best place to start understanding qUnit is its own documentation. FuncUnit / JavaScriptMVC just adds a way to:

  • Organize tests
  • Load tests
  • Run and report results in Envjs

Here's how it works ...

  1. cookbook/qunit.html loads steal.js and tells it to load: cookbook/test/qunit/qunit.js with the following script tag:
    <script type='text/javascript' 
           src='../steal/steal.js?steal[app]=cookbook/test/qunit'>
    </script>
  2. In qUnit.js, the qUnit plugin and tests are loaded.
  3. In cookbook/test/qunit/cookbook_test.js tests are added to be run by qunit.
  4. When the page loads, the tests are run.

When the page is run in Envjs, qUnit does the same 4 steps, but reports the messages on the command line.

As an example of a test, let look at how the findAll test works:

//creates a test
test("findAll", function(){
  //prevents the next test from running
  stop(2000);

  //requests recipes
  Cookbook.Models.Recipe.findAll({}, function(recipes){

    //makes sure we have something
    ok(recipes)

    //makes sure we have at least 1 recipe
    ok(recipes.length)

    //makes sure a recipe looks right
    ok(recipes[0].name)
    ok(recipes[0].description)

    //allows the next test to start
    start()
  });
})

Understanding FuncUnit Tests

FuncUnit adds to qUnit the ability to open another page, in this case cookbook/cookbook.html, perform actions on it, and get information from it.

The cookbook/funcunit.html page works just like the qunit.html page except the 'funcunit' plugin is loaded which provides FuncUnit. FuncUnit is aliased to "S" to highlight the similarity between its API and jQuery's API.

Let take a quick look at a FuncUnit test:

test("create recipes", function(){

  //type Ice in the name field
  S("[name=name]").type("Ice")

  //type Cold Water in the description field
  S("[name=description]").type("Cold Water")

  //click the submit button
  S("[type=submit]").click()

  //wait until the 2nd recipe exists
  S('.recipe:nth-child(2)').exists()

  //Gets the text of the first td
  S('.recipe:nth-child(2) td:first').text(function(text){

    //checks taht it has ice
    ok(text.match(/Ice/), "Typed Ice");
  });

})

Wait ... why is getting the text passed a function?

Functional tests are largely many asynchronous actions (clicks and keypresses) with relatively few checks/assertions. FuncUnit's goal is to provide as readable and linear syntax as possible. FuncUnit statements are actually stored and then run asynchronously. This requires that getting a value from the page happens in a callback function.

For more information on FuncUnit, read its documentation

Testing isTasty

In the Creating Cookbook section of the Getting Started guide, we added an isTasty function to be shown. Lets see how we could unit test that functionality.

At the end of recipe_test.js we'll add code that creates two recipe instances and checks if they are tasty.

test("isTasty", function(){
  var Recipe = Cookbook.Models.Recipe,
      r1 = new Recipe({name: "tea",
                       description: "leaves and water"}),
      r2 = new Recipe({name: "mushroom soup",
                       description: "mushrooms and water"});
  ok(r1.isTasty(), "tea is tasty")
  ok(!r2.isTasty(), "mushroom soup is not tasty")
})

Next, learn how to Compress Cookbook.

© Jupiter IT - JavaScriptMVC Training and Support