Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing return value of spied methods using Jasmine

Full disclosure...this is my first SO question. Please be kind if I leave anything out. ;-)

I'm starting to use Jasmin to test one of my client-side Javascript objects. This object in particular manages the use of a jQuery plugin called DataTables. The managed DataTables component will call a couple custom render methods to determine what to display for a couple columns and I am trying to test those methods.

The short question: How can I test the return value of methods for which I need to put a spy on?

The back story

Here is an shrunk down version of my Javascript object:

function Table(elemId) {
    this._table = $('#'+elemId).dataTable({
        "aoColumnDefs": [
            {
                "fnRender": function(oObj, id) { 
                    return Table.renderIdColumn(oObj, id, lTable); 
                }, 
                "aTargets": ["idColumn"], 
                "bUseRendered": false
            },{
                "fnRender": function(oObj, name) { 
                    return Table.renderNameColumn(oObj, name, lTable); 
                }, 
                "aTargets": ["nameColumn"], 
                "bUseRendered": false
            }
        ],
        "bJQueryUI": true,
        "sPaginationType": "full_numbers"
    });
}

Table.renderIdColumn = function(oObj, id, lTable) {
    return '<input type="checkbox" value="'  id + '" />';
};

Table.renderNameColumn = function(oObj, name, lTable) {
    var id = oObj.aData[0];
    return '<a href="/obj/edit/' + id + '">' + name + '</a>';
};

So when creating a Table object, I need to intercept the call to Table.RenderIdColumn and Table.renderNameColumn so that I can assert the results. Here is what I have in Jasmine so far:

describe("Table", function() {
    var lTable;

    // Write a DOM table that will be rendered by the jQuery DataTable plugin
    beforeEach(function() {
        $('<table id="storeTable"></table>').appendTo('body');
        var headerCellClasses = ["idColumn","nameColumn"];
        var headerRow = $('<tr></tr>');
        $.each(headerCellClasses, function(index, value) {
            headerRow.append('<th class="' + value + '"></th>')
        });
        $('<thead></thead>').append(headerRow).appendTo('#lTable');
        $('<tbody></tbody>').appendTo('#lTable');
    });

    afterEach(function() {
        // First remove DataTables enhancements
        lTable.fnDestroy();
        // Now remove from DOM
        $('#lTable').remove();
    });

    describe("when edit links are shown", function() {
        it("should render a checkbox in ID column", function() {
            spyOn(Table, "renderIdColumn");
            lTable = initializeDataTable();
            var oSettings = lTable._table.fnSettings();
            var id = 1;
            var obj = {
                oSettings: oSettings,
                iDataColumn: 0,
                iDataRow: 0,
                mDataProp: 0,
                aData: oSettings.aoData[0]._aData
            }

            var expected = '<input type="checkbox" value="'+ id +'" />';
            expect(Table.renderIdColumn).toHaveBeenCalledWith(obj, id, lTable);
            var results = Table.renderIdColumn(obj, id, lTable);
            expect(results).toEqual(expected);
        });
        it("should render the name column with a proper link", function() {
            spyOn(Table, "renderNameColumn");
            lTable = initializeDataTable();
            var oSettings = lTable._table.fnSettings();
            var name = "Name";
            var obj = {
                oSettings: oSettings,
                iDataColumn: 3,
                iDataRow: 0,
                mDataProp: 3,
                aData: oSettings.aoData[0]._aData
            }

            var expected = '<a href="/obj/edit/1">Name</a>';
            expect(Table.renderNameColumn).toHaveBeenCalledWith(obj, name, lTable);
            var results = Table.renderNameColumn(obj, name, lTable);
            expect(results).toEqual(expected);
        });
    });

});

function initializeDataTable() {
    // Mock the AJAX call to the server from DataTables plugin
    spyOn($.fn.DataTable.defaults, "fnServerData").andCallFake(function( sUrl, aoData, fnCallback, oSettings ) {
        var json = {
            iEcho: 1,
            iTotalRecords: 1,
            iTotalDisplayRecord: 1,
            aaData: [
                [1, "Name"]
            ]
        }
        fnCallback(json);
    });
    return new Table("lTable");
}

In both test cases, the variable "results" is 'undefined'. I need to test these methods to make sure they are rendering the correct HTML but I can't seem to figure out how to assert the return values. Once I have a spy on the method, it doesn't seem to return anything. I've tried inserting

Table.renderIdColumn.reset();
Table.renderNameColumn.reset();

But neither of those did anything...maybe because my methods are static? FYI, those methods are static because I can't properly assign the "spy" if they are instance methods. The Table constructor calls the DataTables plugin which will cause these methods to be called automatically so I can't construct a Table object and then put a spy on those methods.

like image 961
ShatyUT Avatar asked Apr 23 '12 20:04

ShatyUT


1 Answers

When you write spyOn(Table, "renderIdColumn"), you are (effectively) replacing Table.renderIdColumn with a function that doesn't return anything.

If you want to assert it was called and still return the results of the original, write spyOn(Table, "renderIdColumn").andCallThrough().

The .reset() syntax you mentioned will only reset the spy's internal call counts (IIRC).

like image 177
Mark Rushakoff Avatar answered Oct 01 '22 20:10

Mark Rushakoff