65.9K
CodeProject is changing. Read more.
Home

Testing Public and Private Functions in JavaScript using Jasmine and Chutzpah

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3 votes)

Apr 1, 2014

CPOL

1 min read

viewsIcon

14322

downloadIcon

60

JavaScript Testing

Introduction

There is a big debate among the online community about how to test private functions in a module when we have only access to public functions from it. This article explains about JavaScript testing using module pattern approach. It is useful if you want to test private functions.

Using the Code

Basically, this article has three sections, HTML, JavaScript and testing.

HTML

HTML has simple controls that take two inputs and four buttons that calculate simple calculations such as Add, Subtract, Multiple and Divide.

User can enter desired numeric inputs and user can click on desired buttons to get the calculated output.

@{
    ViewBag.Title = "Home Page";
}
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/publicmodule.js"></script>
<script src="~/Scripts/privatemodule.js"></script>
<h2>Index</h2>


<span id="firstNumberSpan" hidden="true">First Number:</span>
<input type="text" id="firstNumber" hidden="true"/>
<br/>
<span id="secondNumberSpan" hidden="true">Second Number:</span>
<input type="text" id="secondNumber" hidden="true"/>
<br/>
<input id="addBtn" type="button" title="Add" 
value="Add" hidden="true" class="click"/>
<input id="substractBtn" type="button" title="Substract" 
value="Substract" hidden="true" class="click"/>
<input id="multiplyBtn" type="button" title="Multiply" 
value="Multiply" hidden="true" class="click"/>
<input id="divideBtn" type="button" title="Divide" 
value="Divide" hidden="true" class="click"/>

<br/>
Result:
<label id="result">0</label>

<script type="text/javascript">
    publicModule.Init();
</script> 

JavaScript

JavaScript contains two modules:

  1. Public module, which will interact with HTML for all the display logic sides
  2. Private module, which will contain all the logic for calculations, which will return only the required output

By having private module, we can write tests using jasmine and see what they are returning. By this, we know that they will be returns. Here, we will write all the happy path and sad path tests so that it does not break.

In public module, it will manage all the UI related logic and we can write test to check the behavior of the browser.

Public Module
var publicModule = (function ($) {
    //Public function
    var init = function() {
        $("#firstNumber").show();
        $("#firstNumberSpan").show();
        $("#secondNumber").show();
        $("#secondNumberSpan").show();
        $("#addBtn").show();
        $("#substractBtn").show();
        $("#multiplyBtn").show();
        $("#divideBtn").show();
        attachEvents();
    };

    var attachEvents = function () {
        $(".click").click(function () {
            var firstNumber = $("#firstNumber").val();
            var secondNumber = $("#secondNumber").val();
            var result = privateModule.Calculate(this.id, parseInt(firstNumber), parseInt(secondNumber)); //Private function
            $("#result").html(result);
        });
    };

    var api = {
        Init: init, //Public function
    };
    return api;

}(jQuery));
Private Module
/*
All functions in private module will be public
*/
var privateModule = (function ($) {
    var add1 = function (first, second) {
        return first + second;
    };

    var substract1 = function (first, second) {
        return  first - second;
    };

    var multiple1 = function (first, second) {
        return first * second;
    };
    
    var divide1 = function (first, second) {
        return first / second;
    };

    var calculate = function(control, first, second) {
        var result = 0;
        switch (control) {
        case "addBtn":
            result = add1(first, second);
            break;
        case "substractBtn":
            result = substract1(first, second);
            break;
        case "multiplyBtn":
            result = multiple1(first, second);
            break;
        case "divideBtn":
            result = divide1(first, second);
            break;
        default:
        }
        return result;
    };

    var api = {
        Add: add1,
        Substract: substract1,
        Multiply: multiple1,
        Divide: divide1,
        Calculate: calculate
    };
    return api;

}(jQuery));

Testing

I used Jasmine and chutzpah for testing this modules.

Public Module Testing
/// <reference path="Scripts/jquery-1.8.2.js" />
/// <reference path="Scripts/jasmine.js" />
/// <reference path="Scripts/publicmodule.js" />
/// <reference path="Scripts/privatemodule.js" />

describe("public tests:", function () {
        var pathViews = 'Views/Home/Index.cshtml';
    
        function getMarkup(path) {
            var sResult = null;
            $.ajax({
                type: "GET",
                async: false,
                url: path,
                dataType: "text",
                success: function (result, s, x) {
                    sResult = result;
                },
                error: function (result) {
                    console.log("getMarkup failed to load");
                }
            });
            return sResult;
        }
    
        var htmlMarkup = getMarkup(pathViews);

    it("1 load page", function() {
        $("body").html(htmlMarkup);
        publicModule.Init();
    });

    it("2 should be 3 on adding 1 and 2", function () {
        $("#firstNumber").val(1);
        $("#secondNumber").val(2);
        $("#addBtn").click();
        var result = $("#result").html();
        expect(result).toEqual('3');
    });
    
    it("3 should be -1 on substract 1 and 2", function () {
        $("#substractBtn").click();
        var result = $("#result").html();
        expect(result).toEqual('-1');
    });

    it("4 should be 2 on multiply 1 and 2", function () {
        $("#multiplyBtn").click();
        var result = $("#result").html();
        expect(result).toEqual('2');
    });
    
    it("5 should be 2 on divide 1 and 2", function () {
        $("#divideBtn").click();
        var result = $("#result").html();
        expect(result).toEqual('0.5');
    });
}); 
Private Module Testing
/// <reference path="Scripts/jquery-1.8.2.js" />
/// <reference path="Scripts/jasmine.js" />
/// <reference path="Scripts/publicmodule.js" />
/// <reference path="Scripts/privatemodule.js" />


describe("private tests:", function () {

    it("1 should be 3 on adding 1 and 2", function () {
        var result = privateModule.Add(1, 2);
        expect(result).toEqual(3);
    });
    
    it("2 should be 6 on adding 2 and 4", function () {
        var result = privateModule.Add(2, 4);
        expect(result).toEqual(6);
    });
    
    it("3 should be 3 on substract 6 and 3", function () {
        var result = privateModule.Substract(6, 3);
        expect(result).toEqual(3);
    });

    it("4 should be 2 on substract 6 and 4", function () {
        var result = privateModule.Substract(6, 4);
        expect(result).toEqual(2);
    });
    
    it("5 should be 18 on multiply 6 and 3", function () {
        var result = privateModule. Multiply(6, 3);
        expect(result).toEqual(18);
    });

    it("6 should be 24 on multiply 6 and 4", function () {
        var result = privateModule.Multiply(6, 4);
        expect(result).toEqual(24);
    });
    
    it("7 should be 2 on divide 6 and 3", function () {
        var result = privateModule.Divide(6, 3);
        expect(result).toEqual(2);
    });

    it("8 should be 1.5 on multiply 6 and 4", function () {
        var result = privateModule.Divide(6, 4);
        expect(result).toEqual(1.5);
    });

    it("9 should be 3 on calculate function with 1 and 2 for addBtn control", function () {
        var result = privateModule.Calculate("addBtn",1,2);
        expect(result).toEqual(3);
    });
    
    it("10 should be -1 on calculate function with 1 and 2 for substractBtn control", function () {
        var result = privateModule.Calculate("substractBtn", 1, 2);
        expect(result).toEqual(-1);
    });
    
    it("11 should be 2 on calculate function with 1 and 2 for multiplyBtn control", function () {
        var result = privateModule.Calculate("multiplyBtn", 1, 2);
        expect(result).toEqual(2);
    });
    
    it("12 should be 0.5 on calculate function with 1 and 2 for divideBtn control", function () {
        var result = privateModule.Calculate("divideBtn", 1, 2);
        expect(result).toEqual(0.5);
    });
  
});