1

Given a piece of code like below, which is adopted from reinforcejs

var RL = {};
(function(global) {
    var sampleWeighted = function(p) {
        var r = Math.random();
        var c = 0.0;            // cumulative prob
        for(var i=0, n=p.length; i<n; i++) {
            c += p[i];
            if (c >= r) { return i; }
        }
        // assert(false) may happen if sum(p) < 1;
        assert(false, 'wtf');
    };

// many content omitted
...
})(RL);

export default RL;

How can I access sampleWeighted and test it, please? I have tried below

import RL from './rl.js';

it('sampleWeighted', () => {
    expect(RL.sampleWeighted([0.5, 0.5])).toEqual(1);
});

But it complains with error

 FAIL  src/lib/rl.test.js
  ✕ sampleWeighted (1ms)

  ● sampleWeighted

    TypeError: _rl.RL.sampleWeighted is not a function

      at Object.<anonymous> (src/lib/rl.test.js:7:46)
      at process._tickCallback (internal/process/next_tick.js:103:7)

I am new to javascript testing. Currently, I am using create-react-app to setup the testing framework. So what's the proper way to test the above function, please? If you happened to understand what sampleWeighted does, please feel free to comment on it, too.

4
  • Instead of var sampleWeighted, try global.sampleWeighted Commented Oct 10, 2016 at 5:51
  • Do you mean the code was written in a way that's untestable? I tried to follow the original author's convention if possible. Commented Oct 10, 2016 at 5:53
  • Sort of. sampleWeighted is a private function inside IIFE. Code inside it can use it but will not be available to outside code. Commented Oct 10, 2016 at 5:58
  • I suspect there's perhaps some code in the lines you omitted that are important, specifically where sampleWeighted is attached to the RL object. Commented Oct 10, 2016 at 6:22

3 Answers 3

1

sampleWeighted is in the closure of your IIFE (Immediate function invocation syntax)

this way you could keep your method private like private public in Java, notice you have pass the global which is RL, you can treat that as your namespace, and if you want to call sampleWeighted, you need to attach sampleWeighted to RL,

...
RL.sampleWeighted = function() {}
...
Sign up to request clarification or add additional context in comments.

11 Comments

Its IIFE (Immediately Invoked Function Execution) and its not closure, its scope
@Rajesh, Can you confirm the way it's written in var sampleWeighted is untestable? Further, does it mean we shouldn't write code like this at all if it needs tested?
What if I want to call sampleWeighted in another part of the code inside (function(global) {......}, how should it be called? global.sampleWeight looks quite verbose, yes?
@Rajesh isn't IIFE accomplished via clousure? when a function executed inside another function and use the variable from the enclosed function, it is a clousure
@Sean not necessary. If you see OP's code, its an IIFE not wrapped in function. So I assume its used to prevent polluting global scope.
|
1

Write code inside a closure that gives our test code access to the private functions we care about, but then remove that code when we deploy to production. here you can write your code exactly how you’d like it to appear when released, then add on whatever bridge code you need to expose the parts you want to test.

If you’re using a build system, you can do exactly that.

var RL = {};
(function(global) {
    var sampleWeighted = function(p) {
        var r = Math.random();
        var c = 0.0;            // cumulative prob
        for(var i=0, n=p.length; i<n; i++) {
            c += p[i];
            if (c >= r) { return i; }
        }
        // assert(false) may happen if sum(p) < 1;
        assert(false, 'wtf');
    };

    var api = {
        bar: function() {
            // public function `bar` returned from closure
            return "bar"
        }
    };

    /* test-code */
        api._foo = foo
    /* end-test-code */

    return api    

})(RL);

export default RL;

and then you can use "grunt-strip-code"

npm install grunt-strip-code --save-dev

Then enable it inside your Gruntfile:

grunt.loadNpmTasks('grunt-strip-code');


grunt.initConfig({
  strip_code: {
    options: {
      start_comment: "test-code",
      end_comment: "end-test-code",
    },
    your_target: {
      // a list of files you want to strip code from
      src: "dist/*.js"
    }
  }
})


grunt.registerTask("test", [
  "concat",
  "jshint",
  "jasmine"
])
grunt.registerTask("deploy", [
  "concat",
  "strip-code",
  "jshint",
  "uglify"
])

Rerefence - https://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/

2 Comments

that doesn't work, you have to attach your api to the global object. you cannot just return api.
grunt-strip-code looks a bit hackish, is it a best practice to do so?
1

In your IIFE you have not attached sampleWeighted to the passed in object (global). So make sure you do something like below inside of your IIFE:

global.sampleWeighted = sampleWeighted;

Also, in your test file, you need to access that function using:

RL.sampleWeighted

I hope this helps.

2 Comments

It's working. I have this impression that this IIFE way is phasing out with ES6's module system (e.g. export, is that right?
In a way, for some uses...but IIFE is also used if you want to execute a function as soon as the script is parsed, which I'm not sure is something that ES6 modules are designed for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.