3
\$\begingroup\$

When running unit tests in JavaScript it is often useful to gain access to private members. Previously I would add an extra snippet of code inside the class definition (or whatever one calls this in a prototype-based language) to open up the object. Something like this was usually sufficient:

this.internal = function(name) {
    return eval(name);
}

(And before anyone comments of the evils of eval, keep in mind that I'm using only this for testing purposes and it is not included in the final deployment file - I use a grunt script and some regexp to automatically remove it).

This (above) has been working just fine, but I had a niggling feeling that a better (and more general) solution should be possible. I was doing some other work on code instrumentation in the browser and realized that this was a good candidate for some real-time code instrumentation. So, I created this simple functionality for adding some extra functions to a class that allows access to first-level private members:

var CodeInspector = new function() {
    var prefixPattern = /^[a-zA-Z_$][0-9a-zA-Z_$]*/;

    this.instrumentClass = function(classObject, prefix) {
        if(Object.prototype.toString.call(classObject) !== "[object Function]") {
            return false;
        }
        if(prefix === undefined || prefix === null) {
            prefix = "";
        }
        else if(!prefixPattern.test(prefix)) {
            return false;
        }

        var internalCode = "                                    \n\
        this."+prefix+"getPrivate = function(name) {            \n\
            return eval(name);                                  \n\
        }                                                       \n\
        this."+prefix+"setPrivate = function(name, value) {     \n\
            eval(name+' = value');                              \n\
        }                                                       \n\
        this."+prefix+"callPrivate = function(name) {           \n\
            return eval(name+'(arguments)');                    \n\
        }                                                       \n\
        ";

        var code, indexClosingBrace;        

        code = classObject.toString();
        indexClosingBrace = code.lastIndexOf("}");
        code = code.substring(0, indexClosingBrace);
        code += "\n" + internalCode + "\n}";
        return eval("[" + code + "]")[0];
    };
}();

To instrument a class you do this:

MyClass = CodeInspector.instrumentClass(MyClass); 

This will replace the class definition with the instrumented version, which has three new methods to read/get (getPrivate), write/set private members (setPrivate) and to call private functions (callPrivate). Strictly speaking, callPrivate is not necessary since you can always get a reference to the private method via getPrivate and then call it from outside since closure will keep it "inside" the class definition.

I've used it together with QUnit and the Blanket.js code coverage utility and I was pleasantly surprised to find that it didn't mess up the code coverage function.

Here is an example of QUnit test code:

var testClassObject;
var prefix = "__INSTRUMENTED__";

QUnit.begin(function(details) {
    TestClass = CodeInspector.instrumentClass(TestClass, prefix);
    testClassObject = new TestClass();
});

QUnit.test("getPrivate - get value", function(assert) {
    expect(1);
    var privateValue = testClassObject[prefix+"getPrivate"]("_privateValue");
    assert.ok(privateValue, "Value returned");
});

... and this is the simple test class I'm testing:

function TestClass() {
    var _privateValue = "A";

    function _privateFunction() {
        return "B";
    }

    function NestledClass() {
        var _nestledPrivateValue;
    }
}

Note that it only gives access to first level private members. No access is gained to private members of nestled classes. It would probably be possible to instrument second-level classes using regular expressions to decompose the code at text level, but so far I haven't had a need to go beyond the first level.

I'm looking for feedback on the idea, structure and implementation. This is just a quick first attempt. It is working and I've used it successfully to replace my old version with the added code snippet in a couple of projects, but I would like some feedback on possible issues that could restrict its generality, specifically the way the methods are injected before the ending brace.

Are there alternative ways of coding a constructor function that need to be checked for? At the moment I'm only using it to test my own classes, but I would like to make it into a utility that could be used to open up any class. Also, if there are potential cross-browser issues. And, finally, any other feedback or tips on changes, improvements and extensions.

\$\endgroup\$
3
\$\begingroup\$

gain access to private members

Unit tests, as far as I know, is a form of black box testing. The test doesn't concern about the internals of the implementation, only the input and output of the operation. Thus your tests should not be concerned about internal state.

If you really are concerned about internals, either:

  • Make the properties public instead. Specifically, you can use the convention of prefixing _ to the member name to tell users "Ok, I'm public but you should treat me as private."

  • Use accessors (getters and setters). This way, your code isn't full of "debugging" code, but officially usable (albeit unnecessary) code.

  • Another way is to use tools like JSDev. These tools allow you to write debug code and strip them off during production (through a build step). If you don't mind checking-in debug code to your VCS, consider this approach.

You're adding too much complexity in your code to do encapsulation. Given time and effort, nothing in JS is actually private. Native functions can be monkeypatched to hack into enclosed code, dev tools can peer into closures. It's easier to do pseudo-private using the _ private convention.

\$\endgroup\$
  • \$\begingroup\$ You are perfectly right about the definition and ambition concerning unit tests. Of course, one strives to encapsulate the functionality offered by a class as much as possible and then offering up its functionality to the outside with a minimal and easy to use interface. However, I have definitely found a need both for testing private method in isolation and for checking private state variables at different stages of execution. Definition-wize, maybe this is stretching the term unit testing, but it is certainly a practice I find useful. \$\endgroup\$ – instantMartin Mar 28 '15 at 19:03
  • \$\begingroup\$ ...continuing... thanks @joseph-the-dreamer for the tip on JSDev. This had escaped my radar somehow (although obviously I am a follower of Crockford). However, seems (after a brief look-through admittedly) that JSDev is a similar approach to what I have been using with extra code blocks that I remove at deploy. To sum up - I do find it helpful to inspect non-public members without wanting to make them public and I do like the instrumentation approach (which is a type of monkey-patching, no?) and it seems a more clean approach to me - but this is what I'm looking for feedback on :-) \$\endgroup\$ – instantMartin Mar 28 '15 at 19:14
  • \$\begingroup\$ If you really need to test your internals, it's an indication that those internals can/should be extracted into their own class where they could be easily tested, because those results would now be rightfully out in the open by design. \$\endgroup\$ – RubberDuck Mar 29 '15 at 22:37
  • \$\begingroup\$ I agree with you @rubberduck in principle, but I'm writing an API where I want to keep the external interface as minimal as possible at deploy, but at testing I want it wide open. Maybe it would be better to, as you suggest, move internals to a separate class and then offer access to an object of that class during testing and turning this off at deployment. However, my suggested approach makes it possible for me to design the interface they way I want it (minimal), while keeping my options open during testing. \$\endgroup\$ – instantMartin Mar 30 '15 at 8:49
  • \$\begingroup\$ ...continuing... I've also found it helpful to monitor internals when sequences of actions are performed, which I suppose definition-wize is not strictly unit testing but more like integration testing. And that I want to do this to check partial results to make sure that not only is the final result (the output of a unit) the expected, but that the partial results and progression of statuses is the expected. This increases my confidence that I can - in continuous integration fashion - modify or add features and put trust in that my tests will catch both errors in result and in process. \$\endgroup\$ – instantMartin Mar 30 '15 at 8:56
0
\$\begingroup\$

I think that your strategy for unit testing is an overkill. You just need to define the classes in a different way, for example:

function MyClass() {
    this.someField = 0;
}

MyClass.prototype.changeState = function(val) {
    this.someField = 10 + val;
};

Now you can test the changeState method in the following way:

var myClass = new MyClass();
myClass.changeState(7);

// This is a Jasmine syntax but that doesn't matter
expect(myClass.someField).toBe(17);

As you can see, I access the field directly.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.