Routing is a cornerstone of single page applications (SPA’s). In SPA’s, a single page is loaded and the application resides within it. Re-loading the page or navigating to a new page or url is a no-no.Keeping the url static for the lifetime of the SPA is equally bad. Static urls don’t allow the web browser to collect history. Without history the web browser’s back button will just go to whatever site you visited previously – even if you spent 10 minutes judiciously navigating around the SPA. Bookmarks will become meaningless as well.
The Hash
So changing the url is mostly bad. A way around this is using hashes (#) in the URL Path. Hashes were initially used to navigate within a page to different anchor tags ( <a/> ) on the same page. They don’t cause a page refresh and are collected in the web browser’s history. A good example is a page that has a table of contents in the header. Clicking the hyperlinks in the table of contents scrolls down to that particular section on the page which is tagged with anchor. Adding ( or removing ) hashes to the URL does not cause the page to refresh.
So, ../quote/msft will load the TickerComponent with ticker info for Microsoft.
And the “Child Route” ../quote/msft/details will load the TickerContainerComponent as a child element of the TickerComponent.
Child Routes
Child routes allow you to display a child component within a parent component based upon the URL. Typically, the Application component template contains a <router-outlet> directive that displays the initial Component. Parent Components follow a similar pattern. They will likewise have a <router-outlet> directive in which their child component gets loaded.
To clarify I should point out that the ultimate routing table that I ended up using for the Stock Portfolio does not use child routes. I just wanted to give a quick overview of them here as well, I almost used them.
The Routing Module
This is what the Routing Module ( minus the Routing Table ) looks like :
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { APP_BASE_HREF} from '@angular/common';
import { TickerComponent } from './components/ticker/ticker.component';
import { TickerAddComponent } from './components/ticker-add/ticker-add.component';
import { TickerNewsAddComponent } from './components/ticker-news-add/ticker-news-add.component';
import { TickerContainerComponent } from './components/ticker-container/ticker-container.component';
const appRoutes: Routes = [
/* Routing Table */
];
@NgModule({
providers: [
{provide: APP_BASE_HREF, useValue: '/stockapp'}
],
imports: [
RouterModule.forRoot(
appRoutes,
{ enableTracing: true } // <-- debugging purposes only
)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {}
Make sure that you import all of the Components that are loaded by the URL Paths into the Module. The router needs to know about the Components it should load.
What is APP_BASE_HREF?
After migrating routing to a Module I kept getting an error when running my unit tests for the “root” AppComponent:
“Please provide a value for the APP_BASE_HREF token or add a base element to the document.”
Strangely, the error was NOT occuring at runtime. To fix the error I followed these instructions on the Angular website. The router needs to know which “base url” to use when navigating to your routes.
Why a Separate Module?
Best practices dictate that routing should be kept in a separate module from the rest of the application. Another best practices should also be to explain why doing something as such is a best practice. Unfortunately, the official Angular documentation dictates it as such and glosses over the “why”. I’ll take a stab at it :
Seperation of Concerns. Components shouldn’t care what URL invoked them. They should be stand-alone and isolated from such things.
Strategy Pattern. Keeping routing in a seperate module makes it easier to change which URLs launch which Components.
In a hurry?
The finished source code is available on my GitHub repository.
The finished application is up-and-running on Heroku here. Be aware that it is running on a “free” development server so give it a minute or so to spool up.
That’s it for now. Please let me know if you have any questions or comments!
I want live data form my stock portfolio application. Alpha Vantage provides a “free” REST service that supports a plethora of calls for retreiving stock information. If you go here you can make calls against their services. To make calls from your own application however you are going to need to generate your own unique API Key.
CORS
The cool thing about Alpha Advantage is that their REST services are CORS-enabled. CORS-enabled services can be called directly from a web browser without being blocked by cross-domain policies. Typically, a web browser will not allow you to GET data from a domain other then the one your application is running on. So, http://www.mysite.com/myapp can not make calls to http://www.yoursite/yourservice. CORS includes a special headers in the REST HTTP Request, “Access-Control-Allow-Origin” that circumvents this restriction. Prior to CORS and it’s predecessor ( JSONP) I would of had to expose a proxy service on the web application running the stock portfolio application. The proxy service wouldn’t be restricted by cross-domain policies as it would be running on the server. The web-browser would call the proxy service which would in turn call the service residing on another domain.
To get a quote for a stock we’re going to make this REST call against Alpha Vantage. This is what the result looks like :
Although we could store the quote as-is it would be a better idea to create a Strongly-Typed Model for it using Typescript. By using a Strongly-Typed Model :
The signature of the result will be known to downstream consumers – eliminating guesswork and confusion.
Typescript provides validation support. The compiler will notify you if you’re trying to access field on the Model that does not exist.
I created a Ticker Service to fetch the quote from Alpha Vantage’s REST service, wrap it up as a Quote object, and then return it. As REST service calls are asynchoronous the Quote is returned as an Observable. The Ticker Service returns the Observable immediately – even if the REST call hasn’t completed :
The consumer/client subscribes to the Observable – and waits. When the REST service data is finally fetched, “subscribe” is invoked along with a “data” object (the desired Quote). This code snippet is from the Ticker Service’s Unit Test
The finished source code is available on my GitHub repository.
The finished application is up-and-running on Heroku here. Be aware that it is running on a “free” development server so give it a minute or so to spool up.
The requirements presented to me for the coding challenge were pretty sparse :
Create an application for managing “news” articles for a user’s stock portfolio.
It needs to be responsive so it displays nicely on a variety of devices.
Naturally, I had to add my own requirements :
The users’ stock portfolio and news needs to be persistent; a user should not have to re-create their portfolio every time they use the application.
Stock data for the users’ portfolio needs to be refreshed in real-time.
The user should be able to view historical data for a stock in a chart.
Why Components are Awesome
The cool thing about modern web frameworks is that they force you to compartmentalize your stuff into reusable widgets. So, instead of a huge monolithic Javascript file you have a bunch of self-enclosed “widgets”. Each “widget” performs a specific task and has it’s own javascript, html, css, and tests. A “widget” can be used for fetching data or displaying a particular section of markup. This a huge boon when working on large projects on a large team as it allows you to break up the work more easily.
This is a mock-up of what the Components looks like as well as their interaction.
In a hurry?
The finished source code is available on my GitHub repository.
The finished application is up-and-running on Heroku here. Be aware that it is running on a “free” development server so give it a minute or so to spool up.
I’m interviewing right now. One thing that has changed dramatically since I last interviewed is …. the dreaded “Coding Challenge”. What used to consist of a quick whiteboarding excercise has now progressed into the design and development of an entire application. The good news is that employers typically allow you to do it from the comfort of your own home and at your own pace. The bad news is that the challenges can be quite elaborate.
A Stock Portfolio using Angular 6
One of the coding challenges that I was presented with by an employer was to develop a “stock portfolio” application that would allow you to enter stock tickers and news articles. The rules were vague: I just needed to use a newer Javascript Framework ( React, Angular, Vue, etc … ) as well as a Presentation Framework ( Angular Material, Bootstrap, etc… ).
I accepted the challenge, started working on it, and then quickly realized how much cooler the stock portfolio application could be with some additional features. I coul pull in Local Storage, a Live Stock Feed, Charting! Professionally this would be taboo – a bad case of what is know as scope creep. However, being unemployed with some extra time on my hands I felt it was an opportunity to showcase something pretty cool.
In a hurry?
The finished source code is available on my GitHub repository.
The finished application is up-and-running on Heroku here. Be aware that it is running on a “free” development server so give it a minute or so to spool up.
The JQuery UI Autocomplete Widget allows you to return a list of suggestions based upon a query string. The widget works great unless the user does not know what to search for. For example, if the user wants to search through a large database of people using only a single letter (presumably the first letter of the last name) the Autocomplete Widget quickly becomes unwieldy. Either the suggestions need to be truncated or the list of suggestions becomes so large as to be unusable.
To resolve this problem I have extended the JQuery UI Autocomplete Widget and added pagination. Arrows allow the user to cycle through pages of suggestions. Each page of is retrieved using AJAX. Click here for a jsFiddle sample. The sample uses MockJax to simulate the AJAX calls. A complete end-to-end example using ASP.NET MVC is available here.
The Paginated Autocomplete Widget inherits the same API as the Autocomplete Widget from which it is derived. However, there are some differences to be aware of …
First, the web server endpoint that handles the AJAX GET must support three parameters – search, pageSize, and pageIndex. search is the query string provided by the user. pageSize is the number of items to return per page. pageIndex is the page to return.
[HttpGet]
public JsonResult SearchCars(string search, int pageSize=20, int pageIndex=0)
{
// Your logic here.
}
ASP.NET MVC Controller ActionMethod to support the Paginated Autocomplete Widget.
Second, the Paginated Autocomplete expects a JSON response from the web server in a specific format. The Widget requires this format to facilitate pagination. data is an array of text/value objects that will get bound to to the list of suggestions. total is the total number of unpaginated suggestions that the search query returned.
{
data : [
{ text : "Item 1", value : 0 },
{ text : "Item 2", value : 1 },
{ text : "Item 3", value : 2 }
],
total : 1000
}
JSON result expected by the Paginated Autocomplete Widget.
Third, when using the Paginated Autocomplete Widget in Javascript you need to specify sourceUrl. The Widget will automatically generate an AJAX GET to the sourceUrl and retrieve paginated suggestions as necessary. This is different than the Autocomplete Widget where you need to define the source method as well as the underlying AJAX GET. The obfuscation is necessary to facilitate pagination. In addition pageSize is an optional parameter that determines the number of suggestions per page.
Using the Paginated Autocomplete Widget in Javascript.
The Paginated Autocomplete Widget requires JQuery 1.9.1 or newer and JQuery UI 1.10.3 or newer. I have tested it against Internet Explorer 7/8/9/10, Firefox 21, and Chrome 27. It will probably run in other permutations as well. Please let me know if you have any comments or questions.
Javascript is garbage collected but it can still leak memory. The majority of these leaks are caused by Javascript “hooks” into the DOM. There are numerous articles on the internet that provide Javascript memory leak patterns to avoid. Here is a real-world example that I encountered while working with Telerik’s Kendo Grid.
My implementation of the Kendo Grid has a 30 seconds refresh interval. When an interval elapses the Kendo Grid’s Datasource is refreshed from the web server and a dataBound() event handler is triggered. The dataBound() event handler creates elements for the newly fetched data and binds them to the DOM. In the dataBound() event handler we attach several event handlers to these newly created DOM elements. I use these event handlers to show dialogs and such when the user clicks on a cell in the Kendo Grid.
The memory leak occurs when the Kendo Grid is refreshed. On a refresh the Kendo Grid removes the elements from the DOM that were previously added during the last dataBound() invocation. Kendo does not dispose of these elements as expected. Specifically, the event handlers that were previously attached to these elements are not “told” that the elements were removed. This causes a memory leak because the event handlers are retained in memory indefinitely. Every refresh of the Kendo Grid causes another generation of these event handlers to be retained.
Fortunately, fixing the leak is easy. You just need to detach every event handler prior to removing the DOM element to which it is bound. Wen using the Kendo Grid you want to attach your event handlers in the dataBound() event. You want to detach your event handlers in the dataBinding() event. If you use JQuery’s .on() to attach an event handler ( this includes .click(), change(), etc…) you need to use .off() to detach it. If you use JQuery’s .bind() to attach an event handler you need to use .unbind() to detach it.
I’ve created a couple of examples to demonstrate the before and after effects of properly handling this memory leak. The initial source code is taken directly from Kendo’s Demo Site. I have attached a .click() event handler in the Kendo Grid’s dataBound() event handler. The .click() event simply displays an alert pop-up whenever a user clicks on a cell in the table. This is the jsFiddle portraying the memory leak. If I detach the .click() event handler in the Kendo Grid’s dataBinding() event handler the memory leak disappears. This is a jsFiddle with the “fix” applied.
This is the memory footprint of each jsFiddle over a 20 minute period. The green line portrays the memory leak. The red line portrays the “fix”. The memory leakage is around 100-200 kb every 10 minutes.
For a long-running single-page application this is not good. Moving forward I now make memory profiling part of my testing regime. Please let me know if you have any questions.
Notes
The JQuery .empty() function is supposed to recursively remove all child elements as well as detach all event handlers. I have not looked at the Kendo Grid source code. However, if .empty() were being called to dispose of obsolete elements upon a Datasource refresh I would expect this memory leak to not occur.
Javascript Interpretters are not commonly found in the wild – they are built into a Web Browser. Javascript interpretters hosted within a Web Browser expose an API to interact with the DOM.
Command-Line Interpretters
There are, of course, exceptions. Rhino, V8, and Windows Script Host run outside of a Web Browser on a command-line. Command-Line Javascript Interpretters are not bound to a Web Browser and as such they do not have a DOM API – as there is no DOM.
Unit Test Frameworks
There are many different flavors of Unit Test Frameworks. When running a Unit Test each of these Frameworks follows one of two methodologies …
In-Browser
An “In-Browser” Unit Test is run directly within a Web Browser. The Unit Test leverages the Web Browser’s Interpretter and DOM API. The Test Result is displayed as a web page and must be visually inspected – or programatically scraped. This can make automation difficult.
JsUnit and QUnit are two commonly used In-Browser Test Frameworks.
Headless
A “Headless” Unit Test is run using a Command-Line Javascript Interpretter. “Headless” means “No monitor”. The Test Result can theoretically be piped anywhere. This makes it easier to automate.
PhantomJs is a Headless Test Framework that utilizes JavaScriptCore and WebKit. JavaScriptCore is the Javascript Interpretter used by Safari. WebKit is the DOM API / Rendering Engine used by Safari and Chrome.
Keep in mind that although Headless Unit Tests are run outside of a Web Browser they are not really Web Browser agnostic. A Headless Unit Test Framework is only as good as the libraries that it depends on. For example, a passing Unit Test in PhantomJs could theoretically fail in Internet Explorer or Firefox simply because the later use entirely different Javascript and DOM API’s.
How to Write an In-Browser Unit Test
Moving forward I am going to show you how to use QUnit to write In-Browser Unit Tests. Other In-Browser Testing Frameworks are very similar.
Components of a QUnit Unit Test
Writting QUnit Unit Tests requires three things …
QUnit Test Runner (.html)
Unit Tests (.js)
Production Scripts (.js)
QUnit Test Runner
A Test Runner is just a Web Page that links together your Unit Tests and your Production Scripts. When opened in a Web Browser the Test Runner will run the Unit Tests and display the results. Mocked-up HTML Markup can be applied to the Test Runner if the Production Scripts being tested require it. The Mocked-up HTML is reset after each Unit Test is run.
This is what the Test Runner’s markup looks like …
This is what the Test Runner looks like in a Web Browser …
Unit Tests
A Unit Test verifies the functionality of Production Scripts. As your Production Scripts change during development it is the Unit Test’s job to verify that proper functionality is maintained. Each Unit Test invokes a function exposed by your Production Scripts and then Asserts that the outcome is correct. The Assertion could be as simple as verifying a return value or as complicated as verifying that the DOM has been modified apporpriately. Unit Tests are grouped into Modules. Multiple Modules can exist within a single Javascript file. A Unit Test can have any number of Assertions. The number of expected Assertions must be declared.
This is a simple example of a Unit Test. No Production Script is invoked – the Unit Test just asserts that “Hello” is not equal to “World”. “MyUnitTest – Group 1” is an arbitrary name to associate with the Unit Test. The name is displayed in the Test Runner when the Unit Test is run. “1” corresponds to the number of Assertions that are performed by the Unit Test. QUnit supports a handful of different Assertions…
These are the scripts that you want to test. They should be identical to whatever gets deployed in your Production environment.
A Simple Unit Test
If you’re lucky your Production Scripts do not have any external dependencies and do not interact with the DOM.
For example, on a recent project I needed the ability to move an element in an Array from one index to another. To do this I extended the Javascript Array object with two functions – move() and indexOf().
The Production Scripts look like this. First, I check to see if the functions already exist for the Array object. If they don’t I simply add them …
if (!Array.prototype.move) {
// Description : Moves an element in an array.
// Params: old_index - The old index of element.
// new_index - The new index of element.
// http://stackoverflow.com/questions/5306680/move-an-array-element-from-one-array-position-to-another
Array.prototype.move = function (old_index, new_index) {
if (new_index >= this.length) {
var k = new_index - this.length;
while ((k--) + 1) {
this.push(undefined);
}
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
return this; // for testing purposes
};
}
if (!Array.prototype.indexOf) {
// Description : Finds the index of an object in the specified array
// Params: obj - The object.
// start - The index to start looking for the object in the aray.
// http: //stackoverflow.com/questions/1744310/how-to-fix-array-indexof-in-javascript-for-ie-browsers
Array.prototype.indexOf = function (obj, start) {
for (var i = (start || 0), j = this.length; i < j; i++) {
if (this[i] === obj) { return i; }
}
return -1;
}
}
Extensions.js
As for Unit Testing there are two things that I want to verify. First, I want to make sure that the Production Scripts extended the Array object. Here is what the Unit Test for move() looks like …
Next, I want to make sure that the new Array functions work correctly. To do this I simply create an array, invoke the new Array functions against it, and then use QUnit Assertions to verify the result …
The QUnit Test Runner links everything together. Notice that that the Unit Tests are referenced after the Production Scripts. The QUnit Test Runner loads and processes scripts in order of their appearance. It’s important to not reference your Unit Tests first as the Production Scripts that they are testing may not be loaded yet …
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>QUnit Test Suite</title>
<!-- QUnit Dependencies -->
<link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.5.0.css">
<script src="http://code.jquery.com/qunit/qunit-1.5.0.js" type="text/javascript"></script>
<!-- JQuery -->
<script src="http://code.jquery.com/jquery-1.5.min.js" type="text/javascript"></script>
<script src="http://code.jquery.com/ui/1.8.13/jquery-ui.min.js" type="text/javascript"></script>
<!-- Your Production Script(s) -->
<script src="Extensions.js"></script>
<!-- Your Unit Test Script(s) --->
<script src="Extensions.UnitTest.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<!-- The HTML Markup that your Production Script(s) Target -->
</div>
</body>
</html>
TestRunner.html
This is what the Test Runner looks like in a Web Browser when running …
We have been tasked with creating Production Scripts and corresponding Unit Tests to support the required functionality. To do this we’re going to take a Test-Driven approach. Specifically, we’re going to …
Stub out the Production Scripts.
Create the Test Runner.
Create the (Failing) Unit Tests.
Finish the Production Scripts (to Satisfy the Failing Unit Tests).
Stub Out Production Scripts
As a rule of thumb it is very important to keep your Production Script functions as specific as possible. The more complicated the function the more difficult the Unit Test will be to write. You do not want a function that singlehandedly scrapes the DOM, performs a JQuery Ajax POST, and then updates the DOM. Instead, you will want to break down the function into discrete operations.
The application’s functionality can be broken into the following stubbed out functions …
function selectInventoryOnChange (event) {
// TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
}
function getInventoryFromServer ( inventoryCategory ) {
// TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
}
function refreshInventoryTable ( jsonInventoryData ) {
// TODO : Refresh <table> with Inventory Data.
}
function showAddInventoryForm () {
// TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
}
function addNewInventoryClick () {
// TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
}
function addNewInventory ( formData ) {
// TODO : Make AJAX POST to Web Server with <form> data.
}
InventoryManagement.js (Stubbed Out)
As these functions are going to be leveraged by our Unit Tests and the web application we should apply some best practices to make them more portable …
Production Script Best Practices
Namespacing
First, lets encapsulate the functions into a Namespace. Global functions are a no-no as the can overwrite (or be overwritten) by any other Javascript that your application might reference …
var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};
ScottsJewels.InventoryManagement.selectInventoryOnChange = function (event) {
// TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
}
ScottsJewels.InventoryManagement.getInventoryFromServer = function ( inventoryCategory ) {
// TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
}
ScottsJewels.InventoryManagement.refreshInventoryTable = function( jsonInventoryData ) {
// TODO : Refresh <table> with Inventory Data.
}
ScottsJewels.InventoryManagement.showAddInventoryForm = function () {
// TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
}
ScottsJewels.InventoryManagement.addNewInventoryClick = function () {
// TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
}
ScottsJewels.InventoryManagement.addNewInventory = function ( formData ) {
// TODO : Make AJAX POST to Web Server with <form> data.
}
InventoryManagement.js (Stubbed Out using Namespace)
Now instead of invoking our Production Scripts as a global function like this …
addNewInventory ();
We can invoke them through their namespace like this …
Next, let’s restrict access to the functions using the Module Pattern. The Module Pattern permits encapsulation of our functions …
var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};
ScottsJewels.InventoryManagement = (function () {
return {
selectInventoryOnChange : function (event) {
// TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
},
getInventoryFromServer : function ( inventoryCategory ) {
// TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
},
refreshInventoryTable : function ( jsonInventoryData ) {
// TODO : Refresh <table> with Inventory Data.
},
showAddInventoryForm : function () {
// TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
},
addNewInventoryClick : function () {
// TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
},
addNewInventory : function ( formData ) {
// TODO : Make AJAX POST to Web Server with <form> data.
}
};
});
InventoryManagement.js (Stubbed Out using Module Pattern)
Notice that all of the above functions are publically accessible in this Module …
var module = ScottsJewels.InventoryManagement();
module.addNewInventory();
The Module pattern also allows for private functions and data members …
Finally, let’s make the Module a Singleton. The Singleton Pattern provides a common context for all Production Script invocations as well as a smaller memory footprint. It’s as easy as adding () onto the end of the Module declaration. Essentially you are invoking the Module once when it is parsed and loaded …
var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};
ScottsJewels.InventoryManagement = (function () {
return {
selectInventoryOnChange : function (event) {
// TODO : Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
},
getInventoryFromServer : function ( inventoryCategory ) {
// TODO : Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
},
refreshInventoryTable : function ( jsonInventoryData ) {
// TODO : Refresh <table> with Inventory Data.
},
showAddInventoryForm : function () {
// TODO : Display the formAddInventory <form> in a JQuery UI Dialog.
},
addNewInventoryClick : function () {
// TODO : Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
},
addNewInventory : function ( formData ) {
// TODO : Make AJAX POST to Web Server with <form> data.
}
};
})();
InventoryManagement.js (Stubbed Out using Singleton Pattern)
Now you can access the singleton Module now without having to instantiate it …
To allow our Production Scripts to interact with the HTML Markup the HTML Markup needs to be added to the Test Runner’s <div id=”qunit-fixture”> element. Here is the Test Runner with the Production Scripts and HTML Markup added. A reference to the Unit Tests exists as well (although we still need to implement them) …
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>QUnit Test Suite</title>
<!-- QUnit Dependencies -->
<link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.5.0.css">
<script src="http://code.jquery.com/qunit/qunit-1.5.0.js" type="text/javascript"></script>
<!-- JQuery -->
<script src="http://code.jquery.com/jquery-1.5.min.js" type="text/javascript"></script>
<script src="http://code.jquery.com/ui/1.8.13/jquery-ui.min.js" type="text/javascript"></script>
<!-- Your Production Script(s) -->
<script src="InventoryManagement.js"></script>
<!-- Your Unit Test Script(s) --->
<script src="InventoryManagement.Test.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<!-- The HTML Markup that your Production Script(s) Target -->
<div class="manage-inventory">
<select id="selectInventoryCategory">
<option value="" selected="selected">(Select a Category)</option>
<option value="Drinks">Drinks</option>
<option value="Food">Food</option>
<option value="Dessert">Desssrt</option>
</select>
<table id="tableInventory">
<thead>
<tr>
<th>SKU</th>
<th>Name</th>
<th>Quantity</th>
</tr>
</thead>
<tbody/>
<tr>
<td colspan="3" >No Inventory</td>
</tr>
</tbody>
</table>
<button type="button">Add Inventory</button>
<form id="formAddInventory" class="hidden" action="http://localhost/Merchandise/UpdateMerchandise" method="post">
<fieldset>
<input type="text" id="inputSKU" name="sku">
<label for="inputSKU">SKU</label>
<input type="text" id="inputName" name="name">
<label for="inputName">Name</label>
<input type="text" id="inputQuantity" name="quantity">
<label for="inputQuantity">Quantity</label>
</fieldset>
</form>
</div>
</div>
</body>
TestRunner.html
As always there are a couple of “gotchas” that you need to look out for. Specifically …
HTML Markup Synchronization
In Production HTML Markup is served by a Web Server. A Unit Test must operate in isolation of the Web Server and all HTML Markup must be hard-coded in the Test Runner. The downside of this is that you must be vigilant to keep the HTML Markup in your Test Runner synchronized with whatever is generated by your Web Server.
Singleton Module and QUnit Problems
A Singleton Javascript Module and the QUnit Test Runner’s <div id=”qunit-fixture”> are not entirely compatible. If your Module selects elements from the DOM during initialization the elements will point to stale references after the first Unit Test is run.
The 2nd Unit Test will fail because the Test Runner refreshes the HTML Markup in <div id=”qunit-fixture”> after every Unit Test. This will cause $myDiv to point at a stale DOM element.
Create Unit Tests
Next we’re going to create the Unit Tests. To keep it simple we are just going to write a single Unit Test for each Production Script function. Each Unit Test will invoke it’s corresponding Production Script function and assert the results.
Here are the stubbed out Unit Tests along with what they need to assert …
QUnit.module( "ScottsJewels.InventoryManagement",
{
setup : function() {},
teardown : function() {}
});
QUnit.test( "selectInventoryOnChange()", 0, function() {
// Test : Verify that getInventoryaFromServer() is called with the selected inventory as a parameter.
});
QUnit.test( "getInventoryFromServer()", 0, function() {
// Test : Verify the AJAX HTTP GET Request is correct.
// Test : Verify that refreshInventoryTable() is called.
});
QUnit.test( "refreshInventoryTable()", 0, function() {
// Test : Verify the <table> is correctly refreshed in DOM.
});
QUnit.test( "showAddInventoryForm()", 0, function() {
// Test : Verify that the appropriate <form> is displayed.
// Test : Verify that the <form> fields are empty.
});
QUnit.test( "addNewInventoryClick()", 0, function() {
// Test : Verify that the <form> is hidden.
// Test : Verify that addNewInventory() is called with the appropriate data from the <form>.
});
QUnit.test( "addNewInventory()", 0, function() {
// Test : Verify the AJAX HTTP POST Request is correct.
});
InventoryManagement.UnitTest.js (Stubbed Out)
Let’s take a look at the first couple of Unit Tests …
The Unit Test for selectInventoryOnChange() must verify that getInventoryFromServer() is invoked properly.
The Unit Test for getInventoryFromServer() needs to verify that JQuery ajax() was invoked and that it, in turn, invokes refreshInventoryTable().
Fleshing out these two Unit Tests poses a couple of problems …
How does my Unit Test I verify that a Production Script function invokes another function?
How do I verify that JQuery ajax() was invoked correctly?
The same solution can be applied to all three : Mocking.
Mocking Dependenices
Mocking is when you create a new object that mimics the behavior of another object. A “mock” object is oftentimes substituted when the object that it is simulating is unavailable. In Unit Testing dependencies are mocked up when it is not practical to use the actual dependency.
In Javascript functions are objects and easily mockable. This comes in handly when verifying a JQuery ajax() call or asserting the invocation of a nested function. As an example, this is how you might mock up JQuery’s dialog() function for a Unit Test. It’s important to restore the original function implementation after you have conducted you tests …
var jQueryDialog_Orginal;
try {
// Backup.
jQueryDialog_Orginal = jQuery.fn.dialog;
// Implement your own.
jQuery.fn.dialog = function () {
// QUnit assertions...
};
// Run test(s) that implement jQuery Dialog.
}
finally {
// Restore.
jQuery.fn.dialog = jQueryDialog_Orginal;
}
Keep in mind that there are (much) more sophisticated 3rd Party mocking/stubbing Javascript libraries that you can use such as SinonJs and JSMock. This is how you would use mocking to flesh out the Unit Tests for selectInventoryOnChange() …
QUnit.test( "selectInventoryOnChange()", 1, function() {
// Test : Verify that getInventoryaFromServer() is called with the selected inventory as a parameter.
var $event,
$selectInventoryCategory,
original_getInventoryFromServer;
// Set the Inventory Category <select> with "Food" <option>.
$selectInventoryCategory = $("#selectInventoryCategory");
$selectInventoryCategory.val("Food");
// Create a "change" event.
$event = $.Event("change");
$event.target = $selectInventoryCategory;
try {
// Save a reference to getInventoryFromServer() so that it can be restored after the test has completed.
original_getInventoryFromServer = ScottsJewels.InventoryManagement.getInventoryFromServer;
// Now, assign a new function to getInventoryFromServer().
ScottsJewels.InventoryManagement.getInventoryFromServer = function ( inventoryCategory ) {
// Verify that getInventoryFromServer is invoked with the "Food" <option>.
equal( inventoryCategory, "Food" );
}
// Invoke selectInventoryOnChange() and send in the "change" event.
// selectInventoryOnChange() should invoke getInventoryFromServer which was (temporarily) redefined above.
ScottsJewels.InventoryManagement.selectInventoryOnChange ($event);
}
finally {
// Restore getInventoryFromServer().
ScottsJewels.InventoryManagement.getInventoryFromServer = original_getInventoryFromServer;
}
});
InventoryManagement.UnitTest.js
… and getInventoryFromServer() …
QUnit.test( "getInventoryFromServer()", 3, function() {
// Test : Verify the AJAX HTTP GET Request is correct.
// Test : Verify that refreshInventoryTable() is called.
var original_ajax;
try {
// Save a reference to JQuery's ajax() so that it can be restored after the test is complete.
original_ajax = $.ajax;
// Now, assign a new funtion to JQuery's ajax().
$.ajax = function (request) {
// Verify that "Food" was supplied as the ajax "data" parameter.
equal(request.data.inventoryCategory, "Food");
// Verify that the ajax is a GET.
equal(request.type, "GET");
// Verify that refreshInventoryTable() will be invoked if ajax is successful.
equal(request.success, ScottsJewels.InventoryManagement.refreshInventoryTable);
}
// Invoke JQuery's ajax(). This should invoke the (temporarily) re-defined ajax() above.
ScottsJewels.InventoryManagement.getInventoryFromServer ( "Food" );
}
finally {
// Restore JQuery's ajax().
$.ajax = original_ajax;
}
});
InventoryManagement.UnitTest.js
Executing the Test Runner at this time will inevitably lead to lots of failures. This is expected as our Production Scripts are merely stubbed out …
Finish Production Scripts
The last thing to do is to flesh out the Production Script functions. Ironically, the Production Scripts are a lot smaller (and less complicated) than their corresponding Unit Tests. This goes to show that Unit Testing Javascript is not a trivial task …
var ScottsJewels = ScottsJewels || {};
ScottsJewels.InventoryManagement = ScottsJewels.InventoryManagement || {};
ScottsJewels.InventoryManagement = (function () {
$(document).ready(function () {
var $selectInventoryCategory,
$buttonShowAddInventory;
$selectInventoryCategory = $("#selectInventoryCategory");
$buttonShowAddInventory = $("#buttonShowAddInventory");
// Hook up event handlers.
$selectInventoryCategory.bind ( "change", ScottsJewels.InventoryManagement.selectInventoryOnChange);
$buttonShowAddInventory.bind ( "click", ScottsJewels.InventoryManagement.showAddInventoryForm);
});
return {
selectInventoryOnChange : function (event) {
// Get Category from selectInventoryCategory <select> and invoke getInventoryFromServer().
var inventoryCategory;
inventoryCategory = $(event.target).val();
if (inventoryCategory !== "") {
ScottsJewels.InventoryManagement.getInventoryFromServer(inventoryCategory);
}
},
getInventoryFromServer : function ( inventoryCategory ) {
// Make AJAX GET Request to Web Server for Category and invoke refreshInventoryTable() with retreived Data.
$.ajax({
type: "GET",
url : "http://ScottsJewels/InventoryManagement/GetInventory",
data: { inventoryCategory : inventoryCategory },
success: ScottsJewels.InventoryManagement.refreshInventoryTable
});
},
refreshInventoryTable : function ( jsonInventoryData ) {
// Refresh <table> with Inventory Data.
var $tableInventoryBody,
html;
$tableInventoryBody = $("#tableInventory tbody");
html = "";
$(jsonInventoryData).each( function ( index, value ) {
html += "<tr>";
html += "<td class='sku'>" + value.SKU + "</td>";
html += "<td class='name'>" + value.Name + "</td>";
html += "<td class='quanity'>" + value.Quantity + "</td>";
html += "</tr>";
});
$tableInventoryBody
.empty()
.append(html);
},
showAddInventoryForm : function () {
// Display the formAddInventory <form> in a JQuery UI Dialog.
var $formAddInventory;
$formAddInventory = $("#formAddInventory");
$formAddInventory[0].reset();
$formAddInventory.dialog({
buttons: [{ text: "Ok",
click: ScottsJewels.InventoryManagement.addNewInventoryClick
}]});
},
addNewInventoryClick : function () {
// Retreive data from formAddInventory <form>, invoke addNewInventory(), and hide JQuery UI Dialog.
var $formAddInventory,
formData;
$formAddInventory = $("#formAddInventory");
formData = $formAddInventory.serialize();
$formAddInventory.dialog("close");
ScottsJewels.InventoryManagement.addNewInventory( formData );
},
addNewInventory : function ( formData ) {
// Make AJAX POST to Web Server with <form> data.
$.ajax({
type : "POST",
contentType : "application/x-www-form-urlencoded",
url : "http://ScottsJewels/InventoryManagement/AddInventory",
data : formData
});
}
};
})();
InventoryManagement.js
Executing the Test Runner at this point should result in a bunch of Passing Tests. Your Test Runner should look something like this …
Your responsibility as a conscientous Developer doesn’t end here. You should re-run your Unit Tests and verify complicance when …
Your Production Scripts interact with the DOM and the HTML structure is modified.
Your Production Scripts change.
A dependency that your Production Scripts use changes.
There is also a fair share of maintenance that comes along with the Unit Testing. If the above causes any of your Unit Tests to fail you might have to revise your Unit Tests to accommodate the failure. This could be as simple as updating the HTML Markup in your Test Runner or as complicated as refactoring an entire Unit Test.