Click here to Skip to main content
15,887,683 members
Articles / Programming Languages / Javascript

Jasmine - Testing JavaScript

Rate me:
Please Sign up or sign in to vote.
4.88/5 (6 votes)
8 Sep 2016CPOL6 min read 13.8K   10  
This article will help you understand the basics of Jasmine testing framework.

Introduction to Jasmine

Jasmine is an open source automated testing framework based on behavior driven development that help test your JavaScript code. It can run and execute on any platform or wherever JavaScript runs. Jasmine doesn't depend on any browsers, DOM or any other JavaScript framework. But combining Jasmine with frameworks like requireJS and JSCover while testing will help make the tests more effective.

BDD

Behavior-driven development is a development technique which makes use of the combination of Tests driven development and domain driven design. It explains the scenarios in sentences or user stories rather than functional tests. Thus, it helps create a development process which developers, business or any stake holders could easily understand. It increases collaboration between developers, testers and analysts by combining the functional, technical activities and sharing project resources. How far Jasmine belongs to BDD classification is a debatable question.

Setting Up Jasmine

Setting up Jasmine is not a hard job. You may download the latest standalone version from here. For setting up a standalone version, all you have to do is to write the test and give proper references to Jasmine library files, tests and the actual script inside the spec runner. Spec runner is an HTML file in which we have to refer to Jasmine library files, your JavaScript files and Jasmine test file in the order in which they are mentioned. Opening the spec runner will run the rest and you can see the results immediately.

Writing Your First Test

Let's say I have a file helloworld.js and I have to write tests for an addNumbers function which accepts 2 numbers and returns its sum. To start writing the tests, we have to create a JavaScript file and create a test suite first. I will name that file as helloworldTest.js. Open the file in your favorite editor and start creating tests. First, we have to create test suite before the test case. We can have multiple test suites in a file. Below is the code to create a test suite. A test case should reside inside a test suite.

JavaScript
describe('test suite name',function(){
}

Here, describe is the function used to create a test suite. Inside the test suite, the first parameter is the test suite name in quotes. This should specify the purpose of the test suite. After that, inside the callback of that function, we can start writing the test.

JavaScript
describe('Addition specs',function(){
  it('should add 2 valid numbers and returns valid result',   function(){
   expect(addNumbers(1,1)).toEqual(2);
   });
});

Now to run this, include the jasmine library files and source file and then the test file to the spec runner and open the file. This will run the test and show you the result in a user friendly page.

JavaScript
<title>Spec Runner</title>

  <!-- load jasmine -->
  <link rel="stylesheet" type="text/css" href="tools/Jasmine/jasmine.css">
  <script type="text/javascript" src="tools/Jasmine/jasmine.js"></script>
  <script type="text/javascript" src="tools/Jasmine/jasmine-html.js"></script>

  <!-- include source files here... -->
  <script type="text/javascript" src="src/HelloWorld.js"></script>

  <!-- include spec files here... -->
  <script type="text/javascript" src="specs/HelloWorldTest.js"></script>

Matchers

In the last example, we compared the result of our function addNumbers(1,1) to 2 using toEqual(). Here, this toEqual() is called a matcher. We have a few matchers provided by Jasmine itself, here are some of them. All Matchers will return a boolean value back and thus the jasmine will either let the test pass or make it fail.

JavaScript
.toMatch()
.toBeUndefined()
.toBeTruthy()
.toBeFalsy()
.toContain()
.toBeLessThan()
.toBeGreaterThan()
.toThrow()
.toBeCloseTo()
.toBe()

We can negate these matchers by prefixing with .not. For example:

JavaScript
.not.toBeTruthy()

If you think that these built in matchers are not fulfilling your requirement, you may create your own matchers as well.

Custom Matchers

Using this feature of jasmine, we create custom matchers and use them instead of the built in ones. The below code demonstrates how we can create custom matchers for checking if any number is greater than 5.

JavaScript
this.addMatchers({
IsGreaterthanFive: function () {
return this.actual>5 }
});

Now, you may write your test with your own matcher that you just built.

beforeEach and afterEach

If you want to run some code before executing each test, it is painful to write it in every test, in order to avoid that situation and reuse the code, you can include that code inside the beforeEach method. So whenever you are executing any test under the suite, the code inside will also execute before each test. So this is handy when we want to set values to default or so, based on your requirement.

JavaScript
var count=0;
beforeEach(function()
{
  count=count+1;
  console.log("Started executing test no : " + count);
});

If beforeEach executes before every test, as you may have already guessed, afterEach executes just after each test completes its execution. So this can be used to clear any objects that are changed due to the previous test or so, again based on what you want to do.

JavaScript
afterEach(function()
{
console.log(Completed executing test no : " + count);
});

Spy

A functionality of application may be dependent on some other functionality. In order to test a functionality, we may not require to call all the dependent methods which are not in the scope of test. Some of the dependencies like constants are cheap but some functionality like an Ajax call may take a long time to respond due to many reasons like delay in network. If the Ajax call is a dependent of the actual method and you want to keep their implementation away from the test, then you don't have to call them, instead you can make a double of that call using spy.

SpyOn

If we want to create spy of an existing object, we can make use of spyOn method.

JavaScript
spyOn(Obj, "getSumofNumbers")

The .and object of spyOn provides a few options to decide how the spy should behave.

callThrough(): We can combine callThrough() to the spy object will track all the call and will delegate it to the actual implementation. So in-effect, the actual call will get invoked.

JavaScript
spyOn(Obj, "getSumofNumbers").and.callThrough();

returnValue(): Using returnValue(), we can easily return whatever value we want as the ouput of the function. Here in the below example, all calls to the getRandomNumbers method will return 231 as output.

JavaScript
spyOn(Obj, "getRandomNumber").and.returnValue(231);

CallFake(): callFake() simply delegates the function call to the callback function inside the callFake function.

JavaScript
it('should add 2 valid numbers and returns fake method result',function(){
 spyOn(window,'getSumofNumbers').and.callFake(function(){return 5;});
 expect(getSumofNumbers(1,2)).toEqual(5);
//see here 1+2 is not returning actual value 3 instead it gives us //the faked result 5.
});

Stub(): Stub method is used to create a fake of the actual method. While using the callThrough, it actually runs through the implementation of the actual method using the spied object, but stub doesn’t have that implementation. It is useful while testing the function call is really called or not, but can’t test the behavior of the method.

JavaScript
it('should stub returns an undefined obect',function(){
  spyOn(window,'getSumofNumbers').and.callFake(function(){return  5;});
  window.getSumofNumbers.and.stub();
  expect(getSumofNumbers(1,2)).toBe(undefined);
  });
JavaScript
it('should check whether function call is invoked',function(){
  spyOn(window,'getSumofNumbers').and.callFake(function(){return 5;});
  window.getSumofNumbers.and.stub();
  var x = window.getSumofNumbers(1,2);
  expect(window.getSumofNumbers).toHaveBeenCalled(); 
  });

CreateSpy

CreateSpy is basically used to create a spy on a method which is not in the scope of the test context or there is no method to spy on. These methods will not have an implementation, so by using createSpy, we can make the function provide us the result that we want.

If you want your spied method to return a value, you may use andReturn(Value) to tell Jasmine what you want in return.

JavaScript
user.getDept = jasmine.createSpy('getDept()').andReturn('IT');

If you want to define the method body, we can use the andCallFake() method to write the function inside.

JavaScript
user.getDept = jasmine.createSpy('getDept()'). andCallFake( function(){ 
var Dept= 'I'+'T';
Return Dept;
});

CreateSpyObj

CreateSpyObj is used to create an Object with spies of each names that we supply as the property of the object.

JavaScript
car = jasmine.createSpyObj('car', ['accelerate','break','cluch',]);
So created spy will have all the functionalities of spy function 
   
car.break.andReturn('break now');
car.accelerate.andCallFake(function() {return 'accelerated'});

Ignoring Tests Suites

As we already know, we are defining a test suite using a describe function. If for any particular reason we don't want to run the test and the result to show up, we can mark it ignored by prefixing the describe with x.

JavaScript
xdescribe('ignored test suite',function(){/* some code here */});

Ignoring Tests

Similar to test suites, we can ignore an individual test case by prefixing the it with x.

JavaScript
xit('ignored test',function(){ /* some code here */})

Conclusion

Unit tests are an inevitable part of a solution, Jasmine helps us cover the gap of lacking an effective client side test framework. Even though it is effective, the flexibility of the languages like Spying has to be used wisely while writing the test cases, otherwise they may effect the quality of test.

Hope this brief article helped you understand how to write awesome test cases for JavaScript code.

Helpful References

Jasmine source is available in GitHub.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Cognizant
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --