10

In many frameworks, internal function variables are used as private variables, for example

Raphael = (function(){
    var _private = function(a,b) {return a+b;};
    var _public = function(a) {return _private(a,a);}
    var object = {mult2:_public};
    return object;
})();

here, we cannot access from the global namespace the variable named private, as it is an inner variable of the anonymous function in the first line.

Sometimes this function is contains a big Javascript framework, so that it wouldn't pollute the global namespace.

I need to unit tests some object Raphael uses internally (in the above example, I wish to run unit tests on the object private). How can I test them?

edit: I received comments about unit tests which are supposed to test public interfaces.

Let me specify a use case. I'm writing a library called Raphael. This library is supposed to add only a single name to the global namespace, and nothing more. This is a peculiar requirement for Javascript, since Javascript does not have namespaces.

Let's say Raphael uses a linked list. If Javascript had the notion of packages, I would do

require 'linked_list'
Raphael = (function(){/* use linked list */})();

However Javascript does not allow me to do that in any way that wouldn't pollute the global scope with the linked list object! I'm therefore bound to inline linked_list into Raphael's local scope:

Raphael = (function(){
    /* implement linked list */
    var linked_list = function(){/*implementation*/};
})();

And now I want to test linked_list implementation.

Elazar Leibovich
  • 32,750
  • 33
  • 122
  • 169
  • 3
    the idea behind unittests is to test public functions/methods only – Andrey May 05 '10 at 18:36
  • @Andrey, Javascript is not your everyday language! You cannot import other modules in a sane way, so if I'll include a small, say, linked list implementation for Raphael to use, it'll have to be inlined in Raphael's internal function, so that it wouldn't be exposed to the user of Raphael. I can't just `#include` it... – Elazar Leibovich May 05 '10 at 20:12
  • 1
    See: http://stackoverflow.com/questions/2230469/javascript-sandbox-unit-testing http://stackoverflow.com/questions/716207/testing-private-functions-in-javascript http://stackoverflow.com/questions/1881078/testing-javascript-functions-inside-anonymous-functions et all – gnarf May 06 '10 at 00:48

4 Answers4

12

You are still missing the point.

The point of unit testing is to verify that the object's public interface does what is expected of it. Unit tests show how the code works.

The only thing that should be tested is the object's public interface. That way, when you the developer want to change how the object is implemented, you only worry about if the object being tested still does what it is expected of it.

If you feel that the object that is inside that closure needs testing, then test that, but do it externally and then pass it into the closure.

var Raphael= function(listIterator) {
  listIterator.method();
}(new ListIterator());

Spurious hacks, such as shown below, are totally inappropriate (in unit tests or anywhere).

Test functions should be simple, test one thing only, and have one assertion. This can usually happen in three to ten lines of test code.

When you get to the point where your test functions are complicated, as they would be following the approach you are asking about, then either (1) realize that your design might not be what you want it to be and change it so that it is, or (2) change your expectations in the test.

Regarding the code you posted, you forgot var, missed a semicolon, and used two reserved words as identifiers: private and public.

The consequence of not using var is the potential to trigger errors and problems related to various implementations of nonstandard GlobalScopePolluter-type objects ("Object doesn't support this property or method" seen in IE). The consequence of using a FutureReservedWord is SyntaxError. Implementation may provide a syntax extesion to allow FutureReservedWord as identifier, and indeed many do, however it is best to not rely on such extensions and if you got an error, it would be completely your fault.

You mentioned delivering code to users. I suggest that you not do that until you get some more experience and understanding with what you are doing.

// DO NOT USE THIS CODE.
var Raphael = (function(){
    var _private = function(a,b) {return a+b;};
    var _public = function(a) {return _private(a,a);};
    var object = {mult2:_public};
    return object;
})();

var leakedFunction;

// Spurious hack:
//   Give valueOf a side effect of leaking function.
//   valueOf is called by the _private function as a
//   side effect of primitive conversion, where 
//   ToPrimitive(input argument, hint Number) results 
//   in calling valueOf.

function valueOfSnoop(){ 
    leakedFunction = leakedFunction || valueOfSnoop.caller || function(){};
    return 2;
}

var a = {
  valueOf : valueOfSnoop
};

Raphael.mult2(a, 3);
var privateMathod = leakedFunction;
alert(leakedFunction(1, 2));

That example code is only as a demonstration that such thing is possible. Given the choice, it is a poor alternative to the alternatives mentioned earlier; either change your design or change your tests.

Pang
  • 9,564
  • 146
  • 81
  • 122
Garrett
  • 2,936
  • 1
  • 20
  • 22
  • @Garrett, any reason for declaring function(){} instead of just {}. I like it! normally when declaring a `var` I like to code the type just for defensive coding. – Gutzofter May 05 '10 at 21:58
  • If you mean var leakedFunction = function(){}; - I do that because leakedFunction is called later. – Garrett May 06 '10 at 00:19
  • But only after you have reassigned it with this `arguments.callee.caller`. Yes? – Gutzofter May 06 '10 at 02:22
  • Well, yes, `leakedFunction is set to `arguments.callee.caller` inside `valueOf`. I did that in case function.caller or arguments.callee was not available. THe assignment should go inside, and I can reuse a function... Editing... – Garrett May 06 '10 at 05:50
2

Try this:

var adder = function(a,b) {
    return a + b;
}

Raphael = function(fn){
    var _private = function(a,b) {
        fn(a,b);
    }

    var _public = function(a) {
        return _private(a,a);
    }

    var object = {doubleIt: _public};

    return object;
}(adder);

Just a little function injection

Gutzofter
  • 2,003
  • 23
  • 26
  • 1
    The whole idea of having `private` as a var in the function body is not to pollute the global scope. In your solution `Raphael` pollutes the global scope with `adder`. – Elazar Leibovich May 05 '10 at 20:17
  • If you're going to go the route of using "private properties", you either have to have a sensing variable/function to test on the private property. or you have to stick to the public interface. Also you could add another namespace layer to your code and make it private their instead of where its at. Unit test lower level, then run an integration test on the new layer API. – Gutzofter May 05 '10 at 22:03
1

The best solution I came up with:

In source Javascript file use

Raphael = (function(){
// start linked_list
    var linked_list = function() {/*...*/};
// end linked_list
    var object = {mult2:_public};
    return object;
})();

Now, use a script to extract objects between // start ([a-zA-Z_]*) and // end ([a-zA-Z_]*), and unit test the extracted code.

Apparently it is impossible to access variables in the inner scope of a function from an outer scope. As written in the SO question Jason linked to in the comments.

Elazar Leibovich
  • 32,750
  • 33
  • 122
  • 169
0
var Raphael;
var test = true; //or false;

Raphael = (function(){
    var private = function(a,b) {return a+b;};
    var public = function(a) {return private(a,a);}
    var object = {mult2:public};

    if (test) Raphael.private = private;

    return object;
})();
James Westgate
  • 11,306
  • 8
  • 61
  • 68