16

I'm starting to write some javascript tests and trying to figure out what the best approach is for inspecting the private members of a module constructor. For example, in the sample below i'm using the revealing module pattern to expose the public api to my module. I want to test that privateVar is correctly set during the callback of the $.getJSON ajax request.

The second test it('should update privateVar', ...), doesn't work because myModule.privateVar is (intentionally) not in the public api for the module.

So, my question is, What is the best way to test this kind of behaviour without having to make the privateVar part of the public api? Is there a better way to factor this code for testing, or maybe a way to use something like SinonJs to spy on the private member?

define('myModule',
    ['jquery'],
    function ($) {
        var
            myVar = "something",
            privateVar = "something else",

            doSomething = function() {
                return $.getJSON('http://myapi.com/do-something', { requestData : "some data" }, function(response){
                    myVar = response.data.value1;
                    privateVar = response.data.value2;
                });
            };

        return {
            doSomething : doSomething,
            myVar : myVar
        };
    }
);

define('test/test.myModule',
    ['myModule', 'chai', 'sinon', 'mocha'],
    function (myModule, chai, sinon) {

        describe("myModule", function() {
            var expect = chai.expect;

            describe('doSomething', function() {

                var value1 = 'value1 value',
                    value2 = 'value2 value';

                beforeEach(function() {
                    sinon.stub($, 'ajax').yieldsTo('success', {
                        data : { value1 : value1, value2 : value2 }
                    });
                });

                afterEach(function() {
                    $.ajax.restore();
                });

                it('should update myVar', function(done) {
                    myModule.doSomething();
                    expect(myModule.myVar).to.equal(value1);
                    done();
                });

                it('should update privateVar', function(done) {
                    myModule.doSomething();
                    expect(myModule.privateVar).to.equal(value2);
                    done();
                });
            });


        });

    }
);
Robbie
  • 18,750
  • 4
  • 41
  • 45
  • This should shed some light on the topic of testing private members: http://stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones – Aditya Manohar Feb 25 '13 at 12:02
  • @AdityaManohar thanks for the link. In my case i am actually testing a public method, but i want to ensure that it correctly set a non public property. – Robbie Feb 25 '13 at 12:10

4 Answers4

6

What you are talking about here unfortunately requires an integration test, you wish to test that a variable is set as a result of an external operation, You should trust that the external method just works for your test by stubbing it out in your tests as you have done with sinon this takes care of the external call.

What you need to be able to do is to control the conditions of the test (lets say non authenticated and authenticated) then test what the result of the function is in that instance. As a rule I don't normally test private members at all but I do test desired behaviour resulting from known good and bad values..

I also read this a little while ago, which discusses private vars.

krystan honour
  • 6,523
  • 3
  • 36
  • 63
  • It's not really an integration test, because as you point out, i am already stubbing out the ajax call to isolate the SUT, hence the result of the 'external operation' is not being tested, but rather the behaviour of the resulting logic is. – Robbie Mar 02 '13 at 21:55
3

The only way you can access your private variables this way is is to add a public getter that you can later call in your test to verify the state:

In your class:

getPrivateVar : function(){ return privateVar; }

Then add to return statement:

return { getPrivateVar : getPrivateVar, };

Piotr Rochala
  • 7,748
  • 2
  • 33
  • 54
  • 2
    Thanks @rochal, Yes, this would work, but say my privateVar is something secret like an auth token that i don't want exposed. Adding a getter like this is effectively the same as just putting the var in the public api. To be honest i'm not sure if this is a valid concern or not. – Robbie Feb 28 '13 at 00:12
  • 2
    No, you should not modify your code for the tests. A private method is private for a reason. – Stéphane Bruckert Nov 23 '16 at 18:33
0

Actually why test a private variable? It is not possible/difficult. And what is the purpose of that variable? If it is going to be passed on as a argument to function, you can of-course test the function by spying if the function is called with the specific value, which was assigned to the private variable.

0

You can use rewire

let revertBack = renderCtrl.__set__(privateMember,substituteMember);

to revert back just call revertBack(); For more details see https://www.npmjs.com/package/rewire

Hasteq
  • 926
  • 13
  • 21