Notice

╠ This is my personal blog and my posts here have nothing to do with my employers or any other association I may have. It is my personal blog for my personal experience, ideas and notes. ╣
Showing posts with label Behavior Driven Development. Show all posts
Showing posts with label Behavior Driven Development. Show all posts

Sunday, February 5, 2017

Basic introduction of JavaScript testing framework Jasmine [Part - 1]

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.

Development Cycle



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.
Inside Jasmine zip

  1. /src folder: contains the JavaScript source files that you want to test
  2. /lib folder: contains the framework files
  3. /spec folder: contains the JavaScript testing files
  4. SpecRunner.html : is the test case runner HTML file

Please note that actual JavaScript file need to be included before spec/test JavaScript file.

SpecRunner.html

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
      });
});

To disable a spec just simply put a 'x' before keyword 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
  1. HelloWorld = function() {};
  2. HelloWorld.prototype.helloWorld = function(){
  3. return "Hello World!";
  4. }
  5. HelloWorld.prototype.sayHi = function(name) {
  6. return "Hi " + name;
  7. }

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
  1. describe("test HelloWorld", function(){
  2. var helloworld;
  3. beforeAll(function(){
  4. helloworld = new HelloWorld();
  5. });
  6. // 1st Suite for helloWorld
  7. describe("test helloWorld function ", function(){
  8. it("test helloWorld function return 'Hello World!' string", function(){
  9. expect(helloworld.helloWorld()).toEqual("Hello World!");
  10. });
  11. it("do negative test with helloWorld function", function(){
  12. expect(helloworld.helloWorld()).not.toEqual("Hi Andy");
  13. });
  14. });
  15. // 2nd Suite for sayHi
  16. describe("test sayHi function ", function(){
  17. it("test sayHi function return 'Hi Andy' string", function(){
  18. expect(helloworld.sayHi('Andy')).toEqual("Hi Andy");
  19. });
  20. it("do negative test with helloWorld function", function(){
  21. expect(helloworld.sayHi('Andy')).toContain("Andy");
  22. });
  23. });
  24. });

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.