While working on user interface I found that I need to write complex logic in JavaScript
to validate the form data. I started writing validation logic then deploy it in web container and when I find any bug I fix it and redeploy the JavaScript in web container.
At one point I find that I am simply struggling with complex logic and I realize due to this process I am losing time.
Then I started exploring JavaScript testing framework and asked few of user interface experts they told me that they are using JSUnit for testing but one of them recommend me explore Jasmine JavaScript testing framework. So here I'm to share my experience and a brief tutorial / cheat sheet for JUnit users.
Jasmine
Jasmine is a behavior-driven development framework for testing JavaScript code. It does not rely on browsers, DOM, or any JavaScript framework.
You can download it from here. Here we will be using Jasmine version 2.5.2.
Once you extract the zip we will see three folder, one html file and one license file.
Once you extract the zip we will see three folder, one html file and one license file.
Inside Jasmine zip |
- /src folder: contains the JavaScript source files that you want to test
- /lib folder: contains the framework files
- /spec folder: contains the JavaScript testing files
- SpecRunner.html : is the test case runner HTML file
Please note that actual JavaScript file need to be included before spec/test JavaScript file.
Learn the Syntax of Jasmine
Suite
A Jasmine suite is a collection of sub-suites and/or test cases to test the behaviour of JavaScript function or JavaScript Object. This suite is define by
describe
keyword. describe
take two parameters, first parameter is string
and second parameter is JavaScript function
.
describe("Test Suite", function(){
// test case here
});
An inner test suite example
describe("Test Suite", function(){
describe("1st inner Suite" , function() {
});
describe("2nd inner Suite" , function() {
});
});
To disable a suit just simply put a 'x' before keyword
describe
.
describe("Test Suite", function(){
xdescribe("Disable Suite" , function() {
});
describe("2nd inner Suite" , function() {
});
});
Spec
A Jasmine spec represents a test case inside the test suite. This spec is defined by
it
keyword. it
also have two parameters, first parameter is string
and second parameter is JavaScript function
.
describe("Test Suite", function(){
it("Spec", function() {
// test matchers here
});
});
it
.
describe("Test Suite", function(){
xit("Disable Spec", function() {
// test matchers here
});
});
Setup and Tear down
For setup and teardown Jasmine provide four global
beforeAll
, afterAll
, beforeEach
and afterEach
functions.
As these names implied, the
beforeAll
is invoked once before all the spec in describe
are run, and the afterAll
function is called after all spec finish and the beforeEach
is invoked once before each the spec in describe
are run, and the afterEach
function is called after each spec finish.
describe("Test Suite", function(){
beforeAll("Before all spec inside this suite", function() {
// Suite level setup code here
});
beforeEach("Before each spec inside this suite", function() {
// Spec level setup code here
});
it("Spec1", function() {
// test matchers here
});
it("Spec2", function() {
// test matchers here
});
afterEach("After each spec inside this suite", function() {
// Spec level tear down code here
});
afterAll("After all Spec inside this suite", function() {
// Suite level tear down code here
});
});
The this
keyword
Another way to share variables between a
beforeEach
, it
, and afterEach
is through the this
keyword. Each spec's beforeEach
/it
/afterEach
has the this
as the same empty object that is set back to empty for the next spec's beforeEach
/it
/afterEach
.
Available Matchers in Jasmine
Matcher | Purpose |
---|---|
toBe() | passed if the actual value is of the same type and value as that of the expected value. It compares with === operator |
toEqual() | works for simple literals and variables; should work for objects too |
toMatch() | to check whether a value matches a string or a regular expression |
toBeCloseTo() | check if a number is close to another number, given a certain amount of decimal precision as the second parameter.
expect(12.34).toBeCloseTo(12.3, 1); // success
expect(12.34).toBeCloseTo(12.3, 2); // failure
|
toBeDefined() | to ensure that a property or a value is defined |
toBeUndefined() | to ensure that a property or a value is undefined |
toBeNull() | to ensure that a property or a value is null. |
toBeNaN() | this is different from JavaScript's build-in isNaN function. The build-in will return true for non-numeric type,
objects, and arrays. Jasmine's toBeNaN will be positive only if it's the NaN value.
expect(parseInt("hello")).toBeNaN(); // success
|
toBeTruthy() | to ensure that a property or a value is true
|
toBeFalsy() | to ensure that a property or a value is false
|
toContain() | to check whether a string or array contains a substring or an item. |
toBeLessThan() | for mathematical comparisons of less than |
toBeLessThanOrEqual() | for mathematical comparisons of less than or equal |
toBeGreaterThan() | for mathematical comparisons of greater than |
toBeGreaterThanOrEqual() | for mathematical comparisons of greater than or equal |
toBeCloseTo() | for precision math comparison |
toThrow() | for testing if a function throws an exception |
toThrowError() | for testing aspecificthrown exception |
toHaveBeenCalled() | return true if the spy was called |
toHaveBeenCalledWith() | return true if the argument list matches any of the recorded calls to the spy |
toHaveBeenCalledTimes() | return true if the spy was called the specified number of times |
Write and Execute a test
Lets begin with Jasmine , I will be using Eclipse as my intregated development enviroment (IDE) tool. You can use any IDE or notepad editors of your choice.
Step 1
I create a JavaScript project named 'JasminTutorial' and under that project I create a folder named js.
In js folder I copied Jasmine's lib folder and SpecRunner.html file only.
Step 2
Write a JavaScript file which I will be using to test.
HelloWorld.js
- HelloWorld = function() {};
- HelloWorld.prototype.helloWorld = function(){
- return "Hello World!";
- }
- HelloWorld.prototype.sayHi = function(name) {
- return "Hi " + name;
- }
Step 3
Now I create a spec folder under js folder to sperate this test file. I create another JavaScript file named
HelloWorldSpec.js
which is to test HelloWorld.js.
Step 4
Now write the spec.
HelloWorldSpec.js
- describe("test HelloWorld", function(){
- var helloworld;
- beforeAll(function(){
- helloworld = new HelloWorld();
- });
- // 1st Suite for helloWorld
- describe("test helloWorld function ", function(){
- it("test helloWorld function return 'Hello World!' string", function(){
- expect(helloworld.helloWorld()).toEqual("Hello World!");
- });
- it("do negative test with helloWorld function", function(){
- expect(helloworld.helloWorld()).not.toEqual("Hi Andy");
- });
- });
- // 2nd Suite for sayHi
- describe("test sayHi function ", function(){
- it("test sayHi function return 'Hi Andy' string", function(){
- expect(helloworld.sayHi('Andy')).toEqual("Hi Andy");
- });
- it("do negative test with helloWorld function", function(){
- expect(helloworld.sayHi('Andy')).toContain("Andy");
- });
- });
- });
Step 4
Now change in SpecRunner.html , add JavaScript file (HelloWorld.js) and corresponding Spec file (spec/HelloWorldSpec.js).SpecRunner.html |
Now lets see the result.
Now I have changed my development cycle. As a result I'm saving time because I need not have to do redeployment.
David Walsh is Mozilla’s senior web developer, and the core developer for the MooTools
ReplyDeleteJavascript Framework. David’s blog reflects his skills in HTML/5, JS and CSS, and offers a ton
of engaging advice and insight into front-end technologies. Even more obvious is his passion
for open source contribution and trial-and-error development, making his blog one of the
most honest and engaging around.
Website: davidwalsh.name
David Walsh is Mozilla’s senior web developer, and the core developer for the MooTools Javascript Framework. David’s blog reflects his skills in HTML/5, JS and CSS, and offers a ton of engaging advice and insight into front-end technologies. Even more obvious is his passion for open source contribution and trial-and-error development, making his blog one of the most honest and engaging around.
ReplyDeleteWebsite: davidwalsh.name
David Walsh is Mozilla’s senior web developer, and the core developer for the MooTools Javascript Framework. David’s blog reflects his skills in HTML/5, JS and CSS, and offers a ton of engaging advice and insight into front-end technologies. Even more obvious is his passion for open source contribution and trial-and-error development, making his blog one of the most honest and engaging around.
ReplyDeleteWebsite: davidwalsh.name
David Walsh is Mozilla’s senior web developer, and the core developer for the MooTools Javascript Framework. David’s blog reflects his skills in HTML/5, JS and CSS, and offers a ton of engaging advice and insight into front-end technologies. Even more obvious is his passion for open source contribution and trial-and-error development, making his blog one of the most honest and engaging around.
ReplyDeleteWebsite: davidwalsh.name
David Walsh is Mozilla’s senior web developer, and the core developer for the MooTools Javascript Framework. David’s blog reflects his skills in HTML/5, JS and CSS, and offers a ton of engaging advice and insight into front-end technologies. Even more obvious is his passion for open source contribution and trial-and-error development, making his blog one of the most honest and engaging around.
ReplyDeleteWebsite: davidwalsh.name