How to test functions inside javascript closure

2019-07-23 05:57发布

问题:

This seems impossible (and it might be), but I'm trying to get into more TDD and I keep hitting a wall with closures. Say I have the following:

function createSomething(init) {
    function privateMethod(param) {
        return init[param];  //assuming this is more complicated, how can you test it?
    }

    function getData() {
        return init.data;
    }

    function getValue(name) {
        if (name === "privateNode" || typeof name !== "string") {
            return "permissionDenied";
        }
        return privateMethod(name);
    }

    return {
        getData : getData,
        getValue: getValue
    };
}

Putting aside this code probably isn't the best illustration of my point and assuming "privateMethod" is something much more complicated than what is above, is there any way to run unit tests on methods like "privateMethod" or is the best you can do is to test the object created by createSomething? I ask because large parts of my application are hidden inside closures. I'm pretty uninformed in this area, but it seems to me that this is a weak spot for javascript and tdd. fiddle for the code above without tdd is here: http://jsfiddle.net/QXEKd/

回答1:

You can "smuggle" a function (or an Object of functions, variables, etc) out of your closure quite easily by adding an extra parameter to the constructor or hard-coding an early return. The smuggled function should still have the closure from where it was smuggled from unless you're doing something like the evil eval.

It might me worth noting that if you're taking functions out like this for testing purposes only, it may be preferable to comment out or otherwise remove the code to access these before publishing it on your website, so it can't be abused. Also, be aware that this will change depending on how a function is invoked, if you are using it.

For example

function createSomething(init, aaa) {
    function privateMethod(param) {
        return init[param];  //assuming this is more complicated, how can you test it?
    }

    function getData() {
        return init.data;
    }

    function getValue(name) {
        if (name === "privateNode" || typeof name !== "string") {
            return "permissionDenied";
        }
        return privateMethod(name);
    }
    // ----------------------------------
    if(aaa) return privateMethod;
    // ----------------------------------
    return {
        getData : getData,
        getValue: getValue
    };
}

var something = createSomething({
    privateNode : "allmysecrets",
    id : "1234",
    data : {
        stuff : "32t97gfhw3hg4"
    }
}, 1); // passing extra arg to get method

console.log(
    something('id'),
    something('privateNode')
) // 1234 allmysecrets


回答2:

Another possibility is to export a function that adds test-cases. It wouldn't export anything that's meant to be out-of-reach (private).

I mostly use Mocha for testing, so I call "describe()" and "it()" from that function that adds tests. For example, let's say I have created a library called "whatever." I could create a function "test" (or _test, _addTests, ...):

var whatever = (function() {
  var insideClosure = "Ha ha!";
  return {

    // obviously you would have other functions too
    test: function() {
      describe('something', function() {
        it('should work', function() {
          assert.equal(insideClosure, "Ha ha!");
        });
      });
    }
  };
})();

Then my ./test/tests.js would have:

whatever.test();

One obvious bad point with this is the fact that you can't separate pure code and tests (unless maybe by using some kind of deployment workflow).