Tuesday, October 29, 2013

Testing javascript with jasmine - Part 2

Spies:
Jasmine integrates 'spies' that permit many spying, mocking, and faking behaviours  A 'spy' replaces the function it is spying on.
One of the primary aims of unit testing is to isolate a method or component that you want to test and see how it behaves under a variety of circumstances. These might include calls with various arguments - or even none at all, - or whether it calls other methods as it should. Unfortunately, many methods and/or objects have dependencies on other methods and/or objects, such as network connections, data sources, files, and even previously executed methods. This is where mocks come in.

A mock is a fake object that poses as the real McCoy in order to satisfy the inherent dependency(ies) without having to go through the overhead of creating the real object.
Mocks work by implementing the proxy pattern. When you create a mock object, it creates a proxy object that takes the place of the real object.
We can then define what methods are called and their returned values from within our test method. Mocks can then be utilized to retrieve run-time statistics on the spied function such as:
  1. How many times the spied function was called.
  2. What was the value that the function returned to the caller.
  3. How many parameters the function was called with.
In Jasmine, mocks are referred to as spies. There are two ways to create a spy in Jasmine: spyOn() can only be used when the method already exists on the object, whereas jasmine.createSpy() will return a brand new function:


//spyOn(object, methodName) where object.method() is a function
spyOn(obj, 'myMethod')

//jasmine.createSpy(stubName);
var myMockMethod = jasmine.createSpy('My Method');

Using the spyOn() Method


As mentioned above, spyOn() can only be used when the method already exists on the object. For simple tests, this is your best bet.
Our test cases all feature the following Person object. It has a couple of attributes, a getter and setter for the name, and two public methods:

var Person = function() {
    //defaults
    var _age  =  0,
    _name = 'John Doe';
   
    this.initialize = function(name, age) {
        _name = name || _name;
        _age  = age  || _age;
    };
    if (arguments.length) this.initialize();
   
    //getters and setters
    this.getName     = function()      { return _name; };
    this.setName     = function (name) { _name = name; };
   
    //public methods
    this.addBirthday = function()      { _age++; };
    this.toString    = function()      { return 'My name is " + this.getName() + " and I am " + _age + " years old.'; };
};

Say that we want to verify that the toString() method was calling getName(). We would instantiate the Person as usual, but before calling toString(), we would call spyOn(), passing in the person instance and the name of the method that we want to spy on ('getName'). We can then call jasmine matchers to see what happened.

 The simplest test is to check that getName() was in fact called:

describe("Person toString() Test", function() {
    it("calls the getName() function", function() {
        var testPerson = new Person();
        spyOn(testPerson, "getName");
        testPerson.toString();
        expect(testPerson.getName).toHaveBeenCalled();
    });
});


But that's just the beginning. We can run other tests on our spied function, such as what arguments it was called with. The toHaveBeenCalledWith() method accepts a value to be compared against the method's arguments attribute.
Conversely, we can test that the function was called without any parameters by calling toHaveBeenCalledWith() without a value:

describe("Person toString() Test", function() {
    var testPerson;
    beforeEach(function() { testPerson = new Person(); });
    afterEach (function() { testPerson = undefined;    });
   
    it("calls the getName() function", function() {
        spyOn(testPerson, "getName");
        testPerson.toString();
        expect(testPerson.getName).toHaveBeenCalled();
    });
   
    it("Method getName() was called with zero arguments", function() {
        // Ensure the spy was called with the correct number of arguments
        // In this case, no arguments
        expect(testPerson.getName).toHaveBeenCalledWith();
        // this also works
        // expect(testPerson.getName.mostRecentCall.args.length).toEqual(0);
    });
});

Creating Our Own Spy Method

        
Sometimes, it may be beneficial to completely replace the original method with a fake one for testing. Perhaps the original method takes a long time to execute, or it depends on other objects that aren't available in the test context.
Jasmine lets us handle this issue by creating a fake method using jasmine.createSpy(). Here's how to substitute a fake getName() for the real one:

describe("Person toString() Test with Fake getName() Method", function() {
    it("calls the fake getName() function", function() {
        var testPerson = new Person();
        testPerson.getName = jasmine.createSpy("getName spy");
        testPerson.toString();
        expect(testPerson.getName).toHaveBeenCalled();
    });
});


Unlike spyOn(), creating a fake method circumvents the original method so that it is not called during tests. Thus, the alert in getName() below will not appear:

var Person = function() {
    //...
    this.getName = function() {
        alert("You called?");  //won't be called
        return _name;
    };
    //...
};

describe("Person toString() Test with Fake getName() Method", function() {
    it("calls the fake getName() function", function() {
        var testPerson = new Person();
        testPerson.getName = jasmine.createSpy("getName() spy");
        testPerson.toString();
        expect(testPerson.getName).toHaveBeenCalled();
    });
});

Modifying the Fake Method

If your method is being called by another method, you may want it to return something. You can tell Jasmine what to return using the andReturn(value) method:

testPerson.getName = jasmine.createSpy("getName() spy").andReturn("Bobby");


And finally, here's a way to substitute an entirely different method body for the original:

// defining a spy on an existing property: testPerson.getName() calls an anonymous function
testPerson.getName = jasmine.createSpy("getName() spy").andCallFake(function() {
    console.log("Hello from getName()");
    return "Bobby";
});

The above function not only returns "Bobby" each time, but it also logs a message to the console. That would be a little harder to do with the original function.


Spy specific matchers:

When working with spies, these matchers are quite handy:

// assumes obj.method is a function
How to spy on a method?
spyOn(obj, 'method')

Pasitive:
//passes if x is a spy and was called
expect(x).toHaveBeenCalled()
How to verify it was called?
expect(obj.method).toHaveBeenCalled()

//passes if x is a spy and was called with the specified arguments
expect(x).toHaveBeenCalledWith(arguments)
How to verify it was called with specific arguments?
expect(obj.method).toHaveBeenCalledWith('foo', 'bar')

Negative:
//passes if x is a spy and was not called
expect(x).not.toHaveBeenCalled()

//passes if x is a spy and was not called with the specified arguments
expect(x).not.toHaveBeenCalledWith(arguments)

Spies can be trained to respond in a variety of ways when invoked:

//spies on AND calls the original function spied on
spyOn(x, 'method').andCallThrough():
How to have spied method also calls through to the real function?
spyOn(obj, 'method').andCallThrough()

//returns passed arguments when spy is called
spyOn(x, 'method').andReturn(arguments):
How do I fix the return value of a spy?
spyOn(obj, 'method').andReturn('Pow!')


//throws passed exception when spy is called
spyOn(x, 'method').andThrow(exception):

//calls passed function when spy is called
spyOn(x, 'method').andCallFake(function):

Spies can also be retrained after first being invoked:

spyOn(x, 'method').andReturn(value1); ... ;
x.method.andReturn(value2):
//changes the value returned by x.method()

Spies have some useful properties:

//returns number of times spy was called
callCount:
How many times was it called?
obj.method.callCount


//returns argument array from last call to spy.
mostRecentCall.args:
What were the arguments to the last call?
obj.method.mostRecentCall.args

How to reset all the calls made to the spy so far?
obj.method.reset()

How to make a standalone spy function?
var dummy = jasmine.createSpy('dummy')
$('button#mybutton').click(dummy)

How to get all arguments for all calls that have been made to the spy?
obj.method. argsForCall // this is an array
//argsForCall[i] returns arguments array for call i to spy.


Spies are automatically removed after each spec. They may be set in the beforeEach function.

Testing javascript with jasmine - Part 1



What is jasmine.js?
        
Jasmine is a behavior-driven testing framework for JavaScript and an automated testing framework for JavaScript. . It does not require a DOM.


BDD stands for Behaviour-Driven Development
TDD stands for Test Driven Development


TDD:
1.     Write your tests case
2.     Watch them fail case
3.     Make them pass case
4.     Refactor
5.     Repeat

BDD:
·       Establishing the goals of different stakeholders required for a vision to be implemented
·       Involving stakeholders in the implementation process through outside-in software development
·       Using examples to describe the behavior of the application, or of units of code
·       Automating those examples to provide quick feedback and regression testing




Jasmine on your Computer:

         Before you work on jasmine you should get jasmine on your computer.

Get jasmine standalone version here. Then unzip it, and empty out /spec and /src folders. They’re examples, you don’t need it.

In newer version, /lib folder contains jasmine.js, jasmine.css, jasmine-html.js

Let’s playing with jasmine JavaScript.


Let’s understanding Jasmine syntax:
        

describe: (Suite)

A test suite begins with a call to the global Jasmine function "describe” with two parameters: a string and a function.
·       The string is a name or title for a spec suite – usually what is under test.
·       The function is a block of code that implements the suite.


Example:

describe("A suite", function() {
    it("contains spec with an expectation", function() {
        expect(true).toBe(true);
    });
});

it: (specs)

Specs are defined by calling the global Jasmine function it, which, like describe takes a string and a function. The string is a title for this spec and the function is the spec, or test.

A spec contains one or more expectations that test the state of the code under test.



Note:
                Since describe and it blocks are functions, they can contain any executable code necessary to implement the test.
                JavaScript scoping rules apply, so variables declared in a describe are available to any it block inside the suite.

Example:

describe("A suite is just a function", function() {
    var a;
   
    it("and so is a spec", function() {
        a = true;
       
        expect(a).toBe(true);
    });
});


expect: (Expections)

                Expectations are built with the function expect which takes a value, called the actual. It is chained with a Matcher function, which takes the expected value.





Matchers:
                Each matcher implements a boolean comparison between the actual value and the expected value. It is responsible for reporting to Jasmine if the expectation is true or false. Jasmine will then pass or fail the spec.

Negative Matchers:
Any matcher can evaluate to a negative assertion by chaining the call to expect with a not before calling the matcher.

Example:

describe("The 'toBe' matcher compares with ===", function() {
   
        // matches (using postive case)
        it("and has a positive case ", function() {
            expect(true).toBe(true);
        });
   
        // Not matches (using negative case)
        it("and can have a negative case", function() {
            expect(false).not.toBe(true);
        });
});



Built-in Matchers:

//compares objects or primitives x and y and passes if they are equivalent
expect(x).toEqual(y);  

//compares objects or primitives x and y and passes if they are the same object
expect(x).toBe(y);

//compares x to string or regular expression pattern and passes if they match
expect(x).toMatch(pattern);

//passes if x is not undefined
expect(x).toBeDefined();

//passes if x is undefined
expect(x).toBeUndefined();

//passes if x is null
expect(x).toBeNull();

//passes if x evaluates to true
expect(x).toBeTruthy();

//passes if x evaluates to false
expect(x).toBeFalsy();

//passes if array or string x contains y
expect(x).toContain(y);

//passes if x is less than y
expect(x).toBeLessThan(y);

//passes if x is greater than y
expect(x).toBeGreaterThan(y);

//passes if function fn throws exception e when executed
expect(function(){fn();}).toThrow(e);


Built-in Negative Matchers:

Every matcher's criteria can be inverted by prepending “.not”:

//compares objects or primitives x and y and passes if they are not equivalent
expect(x).not.toEqual(y);


Writing New Matchers:
        
         Why new matchers?
                 In jasmine.js having small set of build-in matchers. However, we recommend that you write custom matchers when you want to assert a more specific sort of expectation.
         If we want to create new matchers in jasmine JavaScript, easily we can create it. Custom matchers helps to document the intend of your specs, and can help to remove duplicate code into your specs.

Create custom matchers:
A matcher function receives the actual value as “this.actual”, and zero or more arguments may be passed in the function call. The function should return true if the actual value passes the matcher's requirements, and false if it does not.

Example 1:

beforeEach(function() {
    this.addMatchers({
       
    toBeLessThan: function(expected) {
        var actual = this.actual;
        var notText = this.isNot ? " not" : "";
       
        this.message = function () {
            return "Expected " + actual + notText + " to be less than " + expected;
        }
       
        return actual < expected;
    }
       
    });
});

Ø  Here's the definition of toBeLessThan()
Ø  To add the matcher to your suite, call this.addMatchers() from within a before or it block.
Ø  To customize the failure message, inside the matching function assign this.message to be a function returning the desired message.
Ø  Use this.isNot to make the message correct in the negative case

Example 2:

beforeEach(function () {
    this.addMatchers({
    toBeBetween: function (rangeFloor, rangeCeiling) {
        if (rangeFloor > rangeCeiling) {
            var temp = rangeFloor;
            rangeFloor = rangeCeiling;
            rangeCeiling = temp;
        }
        return this.actual > rangeFloor && this.actual < rangeCeiling;
    }
    });
});


Output 2:

it("is between 5 and 30", function () {
    expect(10).toBeBetween(5, 30);
});

it("is between 30 and 500", function () {
    expect(100).toBeBetween(500, 30);
});

Note:
The describe function is for grouping related specs. The string parameter is for naming the collection of specs, and will be concatenated with specs to make a spec’s full name.

Setup and Teardown:        
Setup and teardown code should be placed in the global beforeEach() and afterEach() methods. These are useful for reinitializing and setting test values between tests.
 The beforeEach() method is called before each spec (it() method) in the describe and afterEach() is called after each spec.

Example:

describe("Stock Portfolio App Tests", function() {
    var presentValue,
    previousValue,
    aPercentChanges;
   
    beforeEach(function() {
        presentValue    = 110;
        previousValue   = 100;
        aPercentChanges = [];
    });
   
    afterEach(function() {
        presentValue    = 0;
        previousValue   = 0;
        aPercentChanges = [];
    });
   
    // The beforeEach() method is called before each spec - it() method in the describe
    it("calcWeeklyPercentChange() should return the change between two numbers as a percentage.", function() {
        var calcWeeklyPercentChange = function(presentValue, previousValue, aPercentChanges) {
            var percentChange = presentValue / previousValue - 1;
            aPercentChanges.push(percentChange);
            return percentChange;
        };
       
        //actually returns 0.10000000000000009!
        var result = calcWeeklyPercentChange(presentValue, previousValue, aPercentChanges);
        expect(result).toBeCloseTo(0.1);
        expect(aPercentChanges.length).toEqual(1);
    });
    // The afterEach() method is called after each spec - it() method in the describe

   
    //So you will get aPercentChanges.length = 0
    it("The aPercentChanges array should now be empty.", function() {
        expect(aPercentChanges.length).toEqual(0);
    });
});

Note:

Nested Describe:

         Jasmine allows to use nested describe.  This allows a suite to be composed as a tree of functions.
Before a spec is executed, Jasmine walks down the tree executing each beforeEach function in order. After the spec is executed, Jasmine walks through the afterEach functions similarly.

Disabling Specs and suites:


Suites and specs can be disabled with the xdescribe and xit functions, respectively. These suites and specs are skipped when run and thus their results will not appear in the results.