Showing posts with label Unit Testing. Show all posts
Showing posts with label Unit Testing. Show all posts

Tuesday, June 04, 2013

Unit Test like a Secret Agent with Sinon.js

The following content comes from the forth module of my Pluralsight course entitled: Front-End First: Testing and Prototyping JavaScript Apps. The rest of the course covers an introduction to Unit Testing, Examples of Hard to Test Code, Mocha (a JavaScript test runner), Grunt (a JavaScript task runner), Mockjax (a way to mock Ajax requests), mockJSON (a way to generate semi-random complex objects for prototyping), and more.

Introduction


“Standalone test spies, stubs and mocks for JavaScript. No dependencies, works with any unit testing framework.”

Sinon.js is a really helpful library when you want to unit test your code. It supports spies, stubs, and mocks. The library has cross browser support and also can run on the server using Node.js.

Spies


“A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. A test spy can be an anonymous function or it can wrap an existing function.”

Example


A test spy records how it is used. It will record how many times it was called, what parameters were used, when it was called, and a bunch of other things. Here you can see an example of creating a spy and I've listed out only a small subset of it’s features such as called, callCount, calledWith, threw, returned, and more.


In addition to just creating a new spy, you can also take an existing function and turn it into a spy. In this example we are taking jQuery and turning it’s ajax method into a spy. Once the spy has been used you can actually pull out one of those instances and verify how that particular call was used. And again, it is important to restore the function back to it’s original state much like we did when we manually stubbed our functions previously.


Mission Impossible: Spy


In the following simple code example we are creating a new ethanHunt spy and passing it to the missionImpossible.start method.

As you can see the start method takes the agent that was passed in and immediately invokes it.

The spy will record how it is used and then you can observe what happened.


At this point we can interrogate ethanHunt if he was called or not, how many times it was called, and a bunch of other questions.


Stubs


“Test stubs are functions (spies) with pre-programmed behavior. They support the full test spy API in addition to methods which can be used to alter the stub's behavior.”

A stub in Sinon.js is also a spy as we've just seen, but it is also a function that has some predefined behavior. A stub is used when we want to fake some functionality so that our system thinks everything is performing normally.

Example


You'll see here that after we have created a stub we can optionally respond to it based on the parameters that are passed to it.


Here we are telling our stub that if "Hello" is passed to it that it should return the string "World" and if we pass "Wuz" to the stub that "Zup?" should be returned.

We can do other things like if "Kapow" is passed to our stub then an exception will be thrown and we can get even more sophisticated and say if an object is passed to the stub it should yieldTo (or invoke) the call function that was passing using the "Howdy" argument. This is some pretty serious and awesome functionality built into these stubs!

Mission Impossible: Stub


In this next mission, if you choose to accept it... we are stubbing out a tape function that will be passed into an assignment method.

The tape will either be passed the string "accept" or "reject" and depending on the answer we want a different result.

With a sinon stub, that is no problem. We can just say tape.withArgs("accept"). returns(new Mission()) and if we wanted to throw a Disintegrate exception if the tape was rejected then we just follow the same pattern... tape.withArgs("reject"). throws("Disintegrate").

If you can't tell already these stubs are really powerful and a great addition to your testing toolkit.


Once we've set up our stub, we can exercise our code as we would normally and the stub will respond with whatever behavior we predefined. Below you'll see that once we pass "accept" that we are getting a Mission object back and if we "reject" the assignment that a Disintegrate exception is thrown.


Stubbed Unit Test


Let’s take an example Twitter unit test and show how we can use a stub to simulate a response from jQuery’s ajax method.


In the before hook we will ask Sinon.js to create us a new stub based off of jQuery’s ajax method and we want to yieldTo (or invoke) the success function from the object that is passed to it. And while we are at it we want to pass our fake twitter data along with the success function.

With that one line of code we have stubbed out the jQuery ajax method and provide fake test data that we can use in our unit test.

Again, it is important to clean up after ourselves so in the after hook at the bottom here we are taking the jQuery.ajax method and calling restore which removes all of the stub behavior from the function,

Mocks


“Mocks (and mock expectations) are fake methods (like spies) with pre-programmed behavior (like stubs) as well as pre-programmed expectations. A mock will fail your test if it is not used as expected.”

Now we finally get to mocks. Mocks are a lot like a stub and a spy, but with a slight twist. With a mock you define up front all of the things you want to expect ( or happen ) then when you are all done with your tests you assert that all those things happened as planned. So, it’s a slightly different way to think than if using a spy or stub by themselves.

Example


In the following code we are defining a mock based off our opts object and we are saying that we expect the call method should only be called once and when it is called that it should have the "Hello World" string argument passed to it.


Then we proceed to run our code that we want tested. You'll see here I’m calling the call method passing the "Hello World" string.

And then at the end you tell the mock object to mock.verify() that all of the expectations you've made previously are valid. If for some reason an expectation was not met, then an exception will occur. And then just like in most of the other examples, we need to clean up after ourselves and call the restore method off of what was mocked.

Mocked Unit Test


Let’s take another look at the Twitter getTweets unit tests again, but this time use a mock instead of a stub.


In the before hook I’m creating a mock of the jQuery object and I’m expecting that the ajax method will only be called one and that it should invoke the success method of the object I pass in with some fakeData I've provided.

Inside my unit test I run the code I want to tests, which is the getTweets method, and then on the callback I call the verify method off of the mock to make sure my expectations have been met.

And as before I restore the object in the after hook.

Fake Timers


“Fake timers is a synchronous implementation of setTimeout and friends that Sinon.JS can overwrite the global functions with to allow you to more easily test code using them.“

Another handy feature of Sinon.s is that you can fake timers! At first this might seem strange, but it turns out it is really powerful and clever.

Example


We first start by asking Sinon.js to useFakeTimers() and save off the clock it gives us. Now let’s take some jQuery animation code that will fadeIn an element slowly onto the screen.


Normally if we wanted to test if this element showed up on the screen we'd either need to provide a callback when the animation is finished or tap into the promise created from the deferred and wait for that to resolve.

However, much like a time lord we can take sinon’s TARDIS, errr... I mean fake timer and tell the clock that we are now 650 milliseconds in the future! And then we can immediately assert that the element is visible without waiting. And of course we will need to restore the clock back to normal when we are done.

Fake Server


“High-level API to manipulate FakeXMLHttpRequest instances.”

Another neat feature that Sinon.js has is a fake server. This is a high level abstraction over the FakeXMLHttpRequest that Sinon.js also provides if you need more granular support.

Example


We can create a fake server from Sinon.js, and we can define that for a GET to the /twitter/api/user.json resource we want to respond with a status code of 200 and the following JSON data.


Then if we called jQuery’s get method with the same URL then we'd get back the data we stubbed out. A key to remember is that you do need to tell the server to respond as we did immediately after we called the get method. And finally we need to restore the server when we are done.

Fake Server Unit Test


Let’s take this technique and add it to our twitter unit test.


In our before hook we create the server and match the resource that our twitter app will be calling and pass back the data we want to stub out. Then we unit test out the getTweets method as we did before, but things don't work out as we expect! Why is that? Well, it is because we are using JSONP as our jQuery ajax datatype. The way JSONP works is that it isn't actually using XMLHttpRequest as a typical Ajax call does. Instead JSONP uses some trickery of injecting a dynamic script tag on your page and a bunch of other things that jQuery tries to hide from you for simplicities sake.

So, in this case using the fake server won't help us. It would be better if we used a stub like we did in the last example.

Conclusion


Hopefully you can see that Sinon.js is a great utility library to help make unit testing a much more effective and terse experience. You'll probably more often than not find yourself making spies and stubs much more often than mocks, but that is really up to how you approach unit testing.

If you enjoyed this content you can get more from my recent Pluralsight course entitled: Front-End First: Testing and Prototyping JavaScript Apps where I cover an introduction to Unit Testing, look at various examples of hard to test code and introduce the following libraries and tools... Mocha, Grunt, Mockjax, amplify.request, mockJSON, etc...

Wednesday, May 15, 2013

Testing and Prototyping JavaScript Applications


I'm pleased to announce that I've finished my first course for Pluralsight entitled Front-End First: Testing and Prototyping JavaScript Apps.

Years ago it was common for the back-end to have code coverage, but having unit tests for client-side JavaScript was difficult, cumbersome, and rare. Thankfully, today that is no longer the case. By using various tools and libraries such as Mocha, Sinon.js, and GruntJS you can easily provide code coverage for your front-end as well.

Historically a front-end developer had to wait until the back-end was complete before they could start truly building a functional User Interface. Thankfully today there are libraries such as Mockjax, AmplifyJS, and mockJSON that can enable you to simulate the interactions with the back-end before its even complete. By doing so, this enables a front-end developer to work independently from the back-end and allows both teams to efficiently work within their speciality.


Tuesday, May 22, 2012

QUnit Composite Addon: Running Multiple jQuery Test Files

Introduction


When you start Unit Testing your application with QUnit you'll notice that you'll have lots of different QUnit files that thoroughly test one feature or component of your system.

Instead of opening each one of those test files and running them separately, wouldn't it be nice if you could launch one file that would run all the tests?

Thankfully, there is a addon for that and it's called the Composite addon!

Composite is a QUnit addon that, when handed an array of files, will open each of those files inside of an iframe, run the tests and display the results as a single suite of QUnit tests. -- https://github.com/jquery/qunit/.../addons/composite

Setup


Setting up the Composite addon is pretty easy. All you really need to do is to get the qunit-composite.js and qunit-composite.css files from the Composite Addon Repository in GitHub and then tell QUnit what test files are a part of your Test Suite! See the following for an example setup.


Running Example


I've taken the Unit Tests from a couple of blog posts I've done recently (filterByData jQuery Plugin: Select by HTML5 Data Attr Value & jQuery :dataAttr Pseudo Selector) and have decided to bundle them together using the QUnit Composite Addon.



NOTE: Normally your URLs will look much cleaner than these in this example. Since I'm running these tests in jsFiddle the resources are pointed to their jsFiddle hash appended with /show so that they will render only the result.

Running from the file:// Protocol


In order for this to work you must host your files in a web server because the Composite addon relies on making AJAX calls to pull in the other QUnit files. If you are trying to run the test from the file:// protocol then you will get an error and the tests will not run. If you want to run the tests from Google Chrome you can enable the allow-file-access-from-files command line parameters.

  • Mac: open /Applications/Google\ Chrome.app --args --allow-file-access-from-files
  • Windows: C:\path\to\chrome.exe --allow-file-access-from-files
  • Linux: /usr/bin/google-chrome --allow-file-access-from-files

Conclusion


Using the QUnit Composite addon is very handy to get a quick high level view of the health of your web application. There is some overhead when running all of the tests at one time, but by making it easier to run all of your tests makes the likelihood of you running them much higher than otherwise.

Monday, June 21, 2010

BDD-Style QUnit Testing ASP.NET MVC’s jQuery Validation

Client-Side Unit Testing



The goal of this blog post is to show how you can utilize some helpful techniques to easily Unit Test your Web Application. In this post I will focus on Unit Testing the Client-Side validation rules that ASP.NET MVC generates. You can apply the following techniques to pretty much any scenario, but since this is something I do, I thought I’d share.

Our sample applications is a Contact Manager. At this point we only have a toolbar with a “New Contact” button. When the button is clicked a “New Contact” dialog will appear with several input fields and a “Save” and “Cancel” button. All of the fields are required, so if the user clicks the “Save” button client-validation should verify that all fields have a value.

ASP.NET MVC Contact ViewModel


First lets take a look at our ViewModel which will drive the rules of our client-side validation.

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace jQueryUnitTestingFormValidation.Models
{
    public class ContactViewModel
    {
        [HiddenInput(DisplayValue = false)]
        public System.Guid Id { get; set; }

        [Required(ErrorMessage = "Name Required")]
        [DisplayName("Name")]
        [StringLength(50, ErrorMessage = "Name must be less than or equal to 50 characters")]
        public string Name { get; set; }

        [Required(ErrorMessage = "Email Required")]
        [DisplayName("E-mail")]
        [StringLength(50, ErrorMessage = "Email must be less than or equal to 50 characters")]
        [DataType(DataType.EmailAddress)]
        [RegularExpression(@"^([a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\s*;?\s*)+$", ErrorMessage = "Email must be valid")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Phone Number Required")]
        [DisplayName("Phone Number")]
        [StringLength(50, ErrorMessage = "Phone must be less than or equal to 50 characters")]
        public string PhoneNumber { get; set; }

        [Required(ErrorMessage = "Date of Birth Required")]
        [DisplayName("Date of Birth")]
        public DateTime? DateOfBirth { get; set; }

        [Required(ErrorMessage = "Required")]
        [DisplayName("Is Married")]
        public bool IsMarried { get; set; }
    }
}

ASP.NET MVC Contact View


Our View is pretty simple. I decided for this sample to not modify the MasterPages that the Templated Helpers use. You’ll see that I’m call the LabelFor, EditorFor, and ValidationMessageFor and organizing them as I wish. If you are interested in a cleaner way to do this you can check out a previous blog entry I wrote entitled Opinionated ASP.NET MVC 2 Template Helpers.

<div id="createDialog" title="New Contact" style="display: none;">
    <% Html.EnableClientValidation(); %> 
    <% using (Html.BeginForm("Create", "Contact", FormMethod.Post, new {@id = "createPost"})) { %>
        <dl>
            <dt><%= Html.LabelFor(m => m.Name) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.Name) %>
                <%= Html.ValidationMessageFor(m => m.Name) %>
            </dd>
            <dt><%= Html.LabelFor(m => m.Email) %></dt>            
            <dd>
                <%= Html.EditorFor(m => m.Email) %>
                <%= Html.ValidationMessageFor(m => m.Email)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.PhoneNumber) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.PhoneNumber) %>
                <%= Html.ValidationMessageFor(m => m.PhoneNumber)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.DateOfBirth) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.DateOfBirth) %>
                <%= Html.ValidationMessageFor(m => m.DateOfBirth)%>
            </dd>
            <dt><%= Html.LabelFor(m => m.IsMarried) %></dt>
            <dd>
                <%= Html.EditorFor(m => m.IsMarried) %>
                <%= Html.ValidationMessageFor(m => m.IsMarried)%>
            </dd>            
        </dl>
    <% } %>    
</div>

JavaScript Contact Revealing Module


The Contact Revealing Module contains the logic to initialize the button and dialog events, post the form to the Controller, etc…

Note: I am utilizing the Revealing Module Pattern for those of you who might not be aware of it. It is very helpful in splitting up your JavaScript code into testable and reusable modules.

var contactCreateModule = (function () {
    var public = {},
        dialogWidth = 800;

    public.createDialog;
    public.createPost;

    public.init = function () {
        public.createDialog = $("#createDialog");
        public.createPost = $("#createPost");

        public.initEventHandlers();
    };

    public.initEventHandlers = function () {
        public.initToolbar();

        public.initDialog();
    };

    public.initToolbar = function () {
        $("#toolbar button").button();

        $("#createContact").click(function () {
            public.displayCreate();
        });
    };

    public.initDialog = function () {
        $(".datePicker").datepicker();

        public.createDialog.dialog({
            autoOpen: false
            , width: dialogWidth
            , modal: true
            , open: validationModule.clearValidationMessages
            , buttons: {
                "Cancel": function () {
                    public.createDialog.dialog("close");
                },
                "Save": function () {
                    if (public.createPost.valid()) {
                        public.createDialog.dialog("close");
                        public.postContact();
                    }
                }
            }
        });
    };

    public.displayCreate = function () {
        public.createDialog
            .dialog("open")
            .find("input:first")
            .focus();
    };

    public.postContact = function (callback) {
        $.ajaxSettings.traditional = true;
        $.ajax({
            url: public.createPost.attr("action"),
            data: public.createPost.serialize(),
            type: "POST",
            success: function (data, textStatus, xhr) {
                public.postContactSuccess(data, textStatus, xhr);
                callback(data && data.Success);
            },
            error: public.postContactError
        });
    };

    public.postContactSuccess = function (data, textStatus, xhr) {
        if (data && data.Success) {
            notificationModule.displayMessage(true, "Your contact has been created!");
        } else {
            notificationModule.displayMessages(data.Success, data.Messages);
        }
    };

    public.postContactError = function (xhr, textStatus, error) {
        var errorMessage = exception ? exception : xhr.statusText;
        notificationModule.displayMessage(false, "There was an error creating your contact: " + errorMessage);
    };

    return public;
} ());

Classic QUnit Style Tests


I initially started this blog post using standard QUnit syntax, but there was something about it that didn’t stand well with me. In particular, I didn’t like how I had a bunch of asserts all lumped together. Later in this article I switch out the classic QUnit-Style with a BDD-Style syntax.

The following are some screenshots from the Unit Tests…


The following is a slightly zoomed in view of the above image. You can see that all the asserts for one particular test are hidden beneath it. You can expand &| collapse the test to see each individual assert.


You can view the unit tests below that generated the above screen shots.
Since I don’t want the $.ajax call to actually occur in my Unit Tests, I swap out the default functionality with a stub function instead in the module setup (which is called before each test). In the module teardown (which is called after each test) I restore the default functionality in case any future test needs it.

var contactWasPosted = false;
module("Contact: Create", {
    setup: function () {
        contactWasPosted = false;

        contactCreateModule.postContactBackup = contactCreateModule.postContact;
        contactCreateModule.postContact = function (callback) {
            contactWasPosted = true;
        };
    },
    teardown: function () {
        contactCreateModule.postContact = contactCreateModule.postContact;
        contactCreateModule.createDialog.dialog('close');
    }
});

test("When New Contact Button Clicked", function () {
    //Arrange

    //Act
    $("#createContact").click();

    //Assert
    ok($("#createDialog:visible").length, "Dialog Should Display");
    ok($("#Name_validationMessage:not(:visible)").length, "Name Validation Should Not Display");
    ok($("#Email_validationMessage:not(:visible)").length, "Email Validation Should Not Display");
    ok($("#PhoneNumber_validationMessage:not(:visible)").length, "PhoneNumber Validation Should Not Display");
    ok($("#DateOfBirth_validationMessage:not(:visible)").length, "DateOfBirth Validation Should Not Display");
    ok($("#IsMarried_validationMessage:not(:visible)").length, "IsMarried Validation Should Not Display");
});

test("When Click Save On an Empty Form", function () {
    //Arrange
    $("#createContact").click();

    //Act
    $(".ui-button-text:contains('Save')").parent().click();

    //Assert
    ok($("#Name_validationMessage:visible").length, "Name Validation Should Display");
    ok($("#Email_validationMessage:visible").length, "Email Validation Should Display");
    ok($("#PhoneNumber_validationMessage:visible").length, "PhoneNumber Validation Should Display");
    ok($("#DateOfBirth_validationMessage:visible").length, "DateOfBirth Validation Should Display");
    ok($("#IsMarried_validationMessage:visible").length, "IsMarried Validation Should Display");
    ok($("#createDialog:visible").length, "Dialog Should Remain Displayed");
});

test("When Click Save On an Complete Form", function () {
    //Arrange
    $("#createContact").click();

    $("#Name").val("xNamex");
    $("#Email").val("tasty@bacon.com");
    $("#PhoneNumber").val("xPhoneNumberx");
    $("#DateOfBirth").val("xDateOfBirthx");
    $("#IsMarried").attr("checked", true);

    //Act
    $(".ui-button-text:contains('Save')").parent().click();

    //Assert
    ok($("#Name_validationMessage:not(:visible)").length, "Name Validation Should Not Display");
    ok($("#Email_validationMessage:not(:visible)").length, "Email Validation Should Not Display");
    ok($("#PhoneNumber_validationMessage:not(:visible)").length, "PhoneNumber Validation Should Not Display");
    ok($("#DateOfBirth_validationMessage:not(:visible)").length, "DateOfBirth Validation Should Not Display");
    ok($("#IsMarried_validationMessage:not(:visible)").length, "IsMarried Validation Should Not Display");
    ok(contactWasPosted, "Contact Should Post");
    ok($("#createDialog:not(:visible)").length, "Dialog Should Be Closed");
});

BDD Style QUnit Tests


After talking more with Dan Mohl (@dmohl) I decided I wanted to try to find a Behavior Driven style of writing QUnit tests. I know there are several other BDD Client-Side Unit Test frameworks out there, but I wanted to keep to the QUnit runner for now.

So, during my research the author of Pavlov, Michael Monteleone (@namelessmike), let me know about his project, which ended up to be exactly what I was looking for.

The following is the output of my tests using QUnit and Pavlov…


Here is a slightly zoomed in view of the QUnit Pavlov test output…


The structure of the Unit Tests is dramatically different from the above classic Unit Tests.

The first thing you’ll notice is that I am extending the Assertion definitions to clean up some of the assert code that I had in my previous Unit Tests.

I still have the same logic in from the above Unit Tests that was in the setup and teardown methods, but now you can find those in the before and after methods.

The syntax of Pavlov is very readable from an English standpoint. You basically describe some scenario in words, and then split it out into code. It was very refreshing once I put it all together.

QUnit.specify.extendAssertions({
    isNotDisplayed: function(actual, expected, message) {
        ok(actual.is(":hidden") || actual.text().length == 0, message || "okay: isNotDisplayed");
    },
    isDisplayed: function (actual, expected, message) {
        ok(actual.is(":visible") || actual.text().length > 0, message || "okay: isDisplayed");
    }
});

QUnit.init({ moduleTestDelimeter: ", it " });
QUnit.specify.globalApi = true;
QUnit.specify("Contact", function () {

    describe("Create", function () {

        var contactWasPosted;

        before(function () {
            contactWasPosted = false;

            contactCreateModule.postContactBackup = contactCreateModule.postContact;
            contactCreateModule.postContact = function (callback) {
                contactWasPosted = true;
            };

            $("#createContact").click();
        });

        after(function () {
            contactCreateModule.postContact = contactCreateModule.postContact;
            contactCreateModule.createDialog.dialog('close');
        });

        describe("When the contact button is clicked", function () {
            it("should display the dialog", function () {
                assert($("#createDialog:visible").length).isEqualTo(1);
            });

            it("should not display name validation", function () {
                assert($("#Name_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display email validation", function () {
                assert($("#Email_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isNotDisplayed();
            });
        });

        describe("When clicking save on an empty form", function () {
            before(function () {
                $(".ui-button-text:contains('Save')").parent().click();
            });

            it("should keep the dialog displayed", function () {
                assert($("#createDialog:visible").length).isEqualTo(1);
            });

            it("should display Name validation", function () {
                assert($("#Name_validationMessage:visible")).isDisplayed();
            });

            it("should display Email validation", function () {
                assert($("#Email_validationMessage:visible")).isDisplayed();
            });

            it("should display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isDisplayed();
            });

            it("should display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isDisplayed();
            });

            it("should display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isDisplayed();
            });
        });

        describe("When clicking save on a completed form", function () {
            before(function () {
                $("#Name").val("xNamex");
                $("#Email").val("tasty@bacon.com");
                $("#PhoneNumber").val("xPhoneNumberx");
                $("#DateOfBirth").val("xDateOfBirthx");
                $("#IsMarried").attr("checked", true);

                $(".ui-button-text:contains('Save')").parent().click();
            });

            it("should not display name validation", function () {
                assert($("#Name_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display email validation", function () {
                assert($("#Email_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display PhoneNumber validation", function () {
                assert($("#PhoneNumber_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display DateOfBirth validation", function () {
                assert($("#DateOfBirth_validationMessage:visible")).isNotDisplayed();
            });

            it("should not display IsMarried validation", function () {
                assert($("#IsMarried_validationMessage:visible")).isNotDisplayed();
            });

            it("should post contact", function () {
                assert(contactWasPosted).isTrue();
            });

            it("should close the dialog", function () {
                assert($("#createDialog:visible").length).isEqualTo(0);
            });
        });

    });

});

Conclusion


The more and more I find myself creating highly dynamic websites, the more I find the need to Unit Test the browser interaction.

I hope you found the above example helpful. I would be interested to hear what tools you use to help Unit Test your client-side code. Please share… it makes us all better :)
You can find some other helpful client-side Unit Testing tools in the Script Junkie article I wrote entitled jQuery Test-Driven Development. In addition I wrote several other jQuery related articles that you can find on Script Junkie.

Note: It was not my intention to exhaustively Unit Test everything in the above example. There are other things I would Unit Test, but to make this example easy to understand in a bite-sized chuck, I limited myself to some simple examples.

Download Source Code


Thursday, September 10, 2009

ASP.NET MVC 1.0 TDD Book Review

AspNetMvcTdd I recently finished reading Emad Ibrahim’s ASP.NET MVC 1.0 Test Driven Development book and I thought I would do a detailed review and share my thoughts and findings.
Before I get into the details of the book, it is important to know that the audience of this book is not for novice programmers or experience non ASP.NET developers. To really get value from this book, you should have some decent exposure to ASP.NET under your belt.
With that said, this book is a very good overview of both ASP.NET MVC and Test Driven Development (TDD). It takes the reader through the process of developing a whole application using the Test First methodology. I found this interesting in that some decisions Emad made initially were later refactored in the book as the application evolved and changed, which is a great way to see the value of TDD and its approach.
This book covers quite a few advanced programming concepts that may be new to you such as various Design Patterns (Strategy, Null Objects, Repository, etc…), Design Principles (Open-Closed Principle, YAGNI, DRY, Inversion of Control, Single Responsibility, Convention Over Configuration, etc…). Emad does a good job about explaining these concepts as he approaches them in the application.
About the same time that I was reading this book, I was in the process of evaluating various tools and frameworks to use for a new ASP.NET MVC v2 project I am working on. I found it interesting that Emad ended up choosing most of the same tools that I had at the time such as: MbUnit, Ninject, Moq, and jQuery. If you are familiar with tools other than the ones he chose at the time of writing this book, you should be able to easily substitute your favorite tool instead. Most of the competitors for these tools have basically the same features.
Since this is a new book, I did run across numerous typos, references to code that wasn’t displayed, refactored code that wasn’t indicated in the text, and several other minor issues, but all in all I knew what Emad was getting at and it wasn’t hard to follow his train of thought. I submitted these inconsistencies to the Errata to hopefully clean up these small issues for future printings of this book. I posted my findings and you can review them online.
I highly recommend downloading the source code from this book (which you can do for free). It is one of the few projects that I’ve seen that has a plethora of Unit Tests to look at and get an idea of how to test your ASP.NET MVC project. I’ve seen numerous other ASP.NET MVC projects that only have a minimal number of Unit Tests and don’t really give you a good idea of how you could get good Code Coverage.
If you are interested in getting a jumpstart into both ASP.NET MVC and Test Driven Development, then I think this is a great book for you to get. However, if you aren’t so sure about TDD and you just want to get up to speed on ASP.NET MVC, then I might recommend you get one of the other beginning ASP.NET MVC books such as:
    Note: A danger of listing books is that I may have missed one ;) If so, please leave a comment listing any of another ASP.NET MVC book published as of the date of this blog post and I will be happy to add it to the list.
I haven’t read any of the above books yet, but I do hope to in the near future and as I do I plan to perform book reviews for those as well.
Thank you Emad for all of your hard work on your book. I enjoyed reading it over my extended Labor Day weekend :)
Updated: You can view the Errata document I put together listing all the inconsistencies, typos, minor issues, etc… online.

Friday, April 17, 2009

JavaScript Unit Testing Part 2: JSSpec

In this post we will examine the 2nd of 4 JavaScript Unit Testing frameworks... JSSpec.

Previous parts of this series...

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

I like this Unit Testing framework a lot more than JsUnit for the following reasons...

  • As you can see from the screenshot below, it has a nice interactive Test Runner
  • The testing API has an easy to read Fluent Interface (a.k.a. It reads like English)
  • The project appears to still be active... the latest version was available on Sep 23, 2008
  • The test files don't need to be hosted in a web server in order to run (unlike JsUnit).

Ok, ok, enough talk. Lets get down to using this framework...

I added functionality to the nasty Pig Latin JavaScript to not only translate words, but also sentences.*

*Note: Yet again, this code is not optimal and it will be refactored in a future post.

function EnglishToPigLatin() {
    this.CONSONANTS = 'bcdfghjklmnpqrstvwxyz';
    this.VOWELS = 'aeiou';
    this.Translate = function(english, splitType) {
        var translated = '';    
        
        console.log('English: ', english);
        var words = english.split(/\s+/);
        console.log('Split Words: ', words);
        for (var i = 0; i < words.length; ++i) {
            console.log('Word ', i, ': ', words[i]);
            translated += this.TranslateWord(words[i]);
            if (i+1 < words.length) translated += ' ';
        }
        console.log('Translated: ', translated);
        console.log('----------');
        
        return translated;
    }
    this.TranslateWord = function(english) {
       /*const*/ var SYLLABLE = 'ay';

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

Like I said before, JSSpec provides a nicer Fluent Interface that makes the tests read more like English. The follow are several sets of Unit Tests to exercise the existing functionality, plus the new feature I added since the last post.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<title>JSSpec results</title>
<link rel="stylesheet" type="text/css" href="JSSpec.css" />
<script type="text/javascript" src="diff_match_patch.js"></script>
<script type="text/javascript" src="JSSpec.js"></script>
<script type="text/javascript" src="PigLatinBad.js"></script>
<script type="text/javascript">// <![CDATA[
describe('Invalid Arguments', {
    before_each : function() { englishToPigLatin = new EnglishToPigLatin(); },
    'Passing Null Should Return Blank': function() {
        value_of(englishToPigLatin.TranslateWord(null)).should_be('');
    },
    'Passing Blank Should Return Blank': function() {
        value_of(englishToPigLatin.TranslateWord('')).should_be('');
    },    
    'Passing 1234567890 Should Return Blank': function() {
        value_of(englishToPigLatin.TranslateWord('1234567890')).should_be('');
    },
    'Passing ~!@#$%^&*()_+ Should Return Blank': function() {
        value_of(englishToPigLatin.TranslateWord('~!@#$%^&*()_+')).should_be('');
    }    
})

describe('Consonant Words', {
    before_each : function() { englishToPigLatin = new EnglishToPigLatin(); },
    'Passing Beast Should Return Eastbay': function() {
        value_of(englishToPigLatin.TranslateWord('beast')).should_be('eastbay');
    },
    'Passing Dough Should Return Oughday': function() {
        value_of(englishToPigLatin.TranslateWord('dough')).should_be('oughday');
    },
    'Passing happy Should Return appyhay': function() {
        value_of(englishToPigLatin.TranslateWord('happy')).should_be('appyhay');
    },
    'Passing question Should Return estionquay': function() {
        value_of(englishToPigLatin.TranslateWord('question')).should_be('estionquay');
    },
    'Passing star Should Return arstay': function() {
        value_of(englishToPigLatin.TranslateWord('star')).should_be('arstay');
    },
    'Passing three Should Return eethray': function() {
        value_of(englishToPigLatin.TranslateWord('three')).should_be('eethray');
    }
})

describe('Vowel Words', {
    before_each : function() { englishToPigLatin = new EnglishToPigLatin(); },
    'apple Should Return appleay': function() {
        value_of(englishToPigLatin.TranslateWord('apple')).should_be('appleay');
    },
    'elijah Should Return elijahay': function() {
        value_of(englishToPigLatin.TranslateWord('elijah')).should_be('elijahay');
    },
    'igloo Should Return iglooay': function() {
        value_of(englishToPigLatin.TranslateWord('igloo')).should_be('iglooay');
    },
    'octopus Should Return octopusay': function() {
        value_of(englishToPigLatin.TranslateWord('octopus')).should_be('octopusay');
    },    
    'umbrella Should Return umbrellaay': function() {
        value_of(englishToPigLatin.TranslateWord('umbrella')).should_be('umbrellaay');
    }    
})

describe('Sentences', {
    before_each : function() { englishToPigLatin = new EnglishToPigLatin(); },
    "Passing 'hello' Should Return 'elloh'": function() {
        value_of(englishToPigLatin.Translate('hello')).should_be('ellohay');
    },
    "Passing 'hello world' Should Return 'elloh orldw'": function() {
        value_of(englishToPigLatin.Translate('hello world')).should_be('ellohay orldway');
    },
    "Passing 'hello world!' Should Return 'ellow orld!w'": function() {
        value_of(englishToPigLatin.Translate('hello world!')).should_be('ellohay orld!way');
    },
    "Passing 'Hello World' Should Return 'elloH orldW'": function() {
        value_of(englishToPigLatin.Translate('Hello World')).should_be('elloHay orldWay');
    },    
    "Passing 'Hello World!' Should Return 'elloH orld!W'": function() {
        value_of(englishToPigLatin.Translate('Hello World!')).should_be('elloHay orld!Way');
    }
})
// ]]></script>
</head>
<body><div style="display:none;"><p>A</p><p>B</p></div></body>
</html>

There are other asserts that can be made that I didn't make use of in the above same. Here are some examples... (taken from the JSSpec Manual)

  • value_of('Hello'.toLowerCase()).should_be('hello');
  • value_of([1,2,3]).should_be([1,2,3]);
  • value_of(new Date(1979,03,27)).should_be(new Date(1979,03,27));
  • value_of([]).should_be_empty();
  • value_of(1 == 1).should_be_true();
  • value_of(1 != 1).should_be_false();
  • value_of("Hello").should_have(5, "characters");
  • value_of([1,2,3]).should_have(3, "items")
  • value_of({name:'Alan Kang', email:'jania902@gmail.com', accounts:['A', 'B']}).should_have(2, "accounts");
  • value_of([1,2,3]).should_have_exactly(3, "items");
  • value_of([1,2,3]).should_have_at_least(2, "items");
  • value_of([1,2,3]).should_have_at_most(4, "items");
  • value_of([1,2,3]).should_include(2);
  • value_of([1,2,3]).should_not_includ
  • value_of({a: 1, b: 2}).should_include("a");
  • value_of({a: 1, b: 2}).should_not_include("c");

As you can see, this is a very powerful easy to read and execute testing framework. Of those frameworks I have tested thus far, I recommend it above the others (at this point I've only covered JsUnit).

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.