Jasmine is a behavior-driven development framework for testing JavaScript code with no dependencies, and can run in Node or browser.
In a BDD style test you describe your code and tell the test what it should be doing. Then you expect your code to do something.
describe ( 'presentation.js' , function(){
//what it should do
it ( 'should be informative', function(){
//expect something
expect(
presentation.inform()
).toBeTruthy();
)};
} );
Folder Structure
jasmine
|--lib
| |--jasmine.js
| |--jasmine-jquery.js
|--spec
| |--someSpec.js
SpecRunner.html
Source file would be under js folder
//...following the default script
//source code
//spec files, test cases
toBe( 'expected' ) //exact compare (===)
toEqual( 'expected' ) //more general compare, can compare objects
toBeDefined( ) //checks if var is not undefined
toMatch( /regex/ ) //matches against regex
toBeTruthy( ) //checks if var is truthy
toBeLessThan( number ) //checks if value is less than number
Any matcher can be "reversed" by including the not keyword.
expect( 5 ).not.toEqual( 3 );
A set of matchers and functions that help you test DOM elements
toBeInDOM()
toBeVisible()
toContainHtml(string)
toHaveClass(className)
Spies are utilities provided by jasmine to monitor function calls.
A spy can stub any function and tracks calls to it and all arguments.
use jasmine.createSpy() with jasmine.clock() jasmine.ajax() to handle asynchronous event
To use it we tell Jasmine to spyOn something. By default they are similar to mocks in other unit testing frameworks.
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');
foo.setBar(123);
foo.setBar(456, 'another param');
});
}
Using a spy
describe("A spy", function() {
...
it("tracks that the spy was called", function() {
expect(foo.setBar).toHaveBeenCalled();
});
}
validates that the method was called
expect( myObj.showOtherDiv ).toHaveBeenCalled();
validates that the method was called with specific arguments
expect( myObj.showOtherDiv ).toHaveBeenCalledWith( args );
Avoid these situations:
A general lack of structure; almost everything happens in one big function, and a lot of anonymous functions.
An example of not testable code
$(document).ready(function(){
var div1 = $('div.one'),
itemCount = getItemCount( 'li.item' );
div1.on('click',function(){
...
});
div1.on('change', function(){
...
});
...
function getItemCount( selector ){
return $( selector ).length;
}
});
Why not testable
Make it more testable
$().ready(function(){
//a bunch of codes
//doing first task
//doing second task
})
function init() {
do1();
do2();
...
}
function do1 () {}
function do2 () {}
...
div1.on('click',function(){
...
});
function handleClick () {}
div1.on('click', handleClick);
function do1() {
var someImportantVariable = value;
}
var app = {
someImportantVariable = value,
...
}
var app = {
init : function(){
div1.on('click', this.handleDivClick);
div1.on('change', this.handleDivChange);
...
this.itemCount =
this.getItemCount( 'li.item' );
},
itemCount: 0,
getItemCount : function( selector ){
return $( selector ).length;
},
handleDivClick : function( e ){ ... }
handleDivChange : function() { ...}
...
};
Good Practices on Writing test cases
General guidelines
Make each test isolated and independent to all the others
Any given behaviour should be specified in one and only one test
The execution/order of execution of one test cannot affect the others
Each test should only test one code unit
Don't test multiple concerns in the same test
it('should properly validate and update error message', function(){...})
it('should properly valudate', function(){...})
it('should update error message', function(){...})
Setup properly the actions that apply to all the tests involved
beforeEach, afterEach, beforeAll, afterAll
Name unit tests clearly and consistently
All the describe and it should be informative
Maintainence