Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing web-browser independent JavaScript

I have client-side JavaScript that does not interact with the DOM or the web-browser in any way. I would like to unit-test the functionality of this code (which is just a fancy database/buffer) in Travis-CI without starting up a web-browser. Command-line JavaScript made me think I need node.js. I looked through various unit-testing libraries and decided on Mocha for its simplicity, however testing a browser-based module/class seems to be excessively difficult with a node.js based library.

Specifically, I want to test this (simplified) browser JavaScript valid code:

// I need this NameSpace to organise my code and isolate from other code
var Nengo = {};

Nengo.DataStore = function(dims) {
    this.times = [];
    this.data = [];
    for (var i=0; i < dims; i++) {
        this.data.push([]);
    }
}

Nengo.DataStore.prototype.push = function(row) {
    this.times.push(row[0]);
    for(var i = 0; i < this.data.length; i++){
        this.data[i].push(row[i+1]);
    }
}

When I try to test in Node.js, I can't import the idea of the Nengo namespace properly. This test can't even run:

// get the datastore code from the folder below
require("../simple_data")
var assert = require("assert")

describe("DataStore", function() {
    var data_store = new Nengo.DataStore(2);

    it("accepts data", function() {
        data_store.push([0.0, 1.1, 1.2])
        assert.deepEqual(data_store.data, [[1.1], [1.2]])
    });
});

It fails with the following error:

/home/saubin/javascript_test/test/simple_test.js:5
    var data_store = new Nengo.DataStore(2);
                     ^
ReferenceError: Nengo is not defined
    at Suite.<anonymous> (/home/saubin/javascript_test/test/simple_test.js:5:22)
    at context.describe.context.context (/usr/local/lib/node_modules/mocha/lib/interfaces/bdd.js:49:10)
    at Object.<anonymous> (/home/saubin/javascript_test/test/simple_test.js:4:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at /usr/local/lib/node_modules/mocha/lib/mocha.js:192:27
    at Array.forEach (native)
    at Mocha.loadFiles (/usr/local/lib/node_modules/mocha/lib/mocha.js:189:14)
    at Mocha.run (/usr/local/lib/node_modules/mocha/lib/mocha.js:422:31)
    at Object.<anonymous> (/usr/local/lib/node_modules/mocha/bin/_mocha:398:16)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:935:3

One way I have thought of solving this is abandoning node and outputting the test results to the DOM of a headless browser and getting the results, but this seems like a lot of excessive overhead. Can I change the structure of my code to be compatible with Node.js? Is there some other solution that I'm not seeing due to lack of knowledge in the area?

like image 592
Seanny123 Avatar asked Feb 11 '26 09:02

Seanny123


1 Answers

You can make your code "node.js-aware" so it puts global definitions into the actual global context while still remaining completely compatible with the browser environment:

if (typeof window !== "undefined") {
    // in browser, define global to be an alias for window
    // so global can be used to refer to the global namespace in
    // both the browser and node.js
    var global = window;
}

global.Nengo = {};

(function() {
    var Nengo = global.Nengo;

    Nengo.DataStore = function(dims) {
        this.times = [];
        this.data = [];
        for (var i=0; i < dims; i++) {
            this.data.push([]);
        }
    }

    Nengo.DataStore.prototype.push = function(row) {
        this.times.push(row[0]);
        this.data.push(row.slice(1));
    }
})();

Then, just remember that ANY global definitions, must be explicitly assigned to the global namespace. In node.js, this will assign them to the actual global namespace in node.js. In the browser, this will assign them to the window object which is the global namespace in the browser.

like image 111
jfriend00 Avatar answered Feb 13 '26 02:02

jfriend00