Sinon stub private variable in typescript?

5,372 views
Skip to first unread message

Swee Tim

unread,
Feb 22, 2017, 9:29:59 AM2/22/17
to Sinon.JS
As I know, sinon dont stub properties, only method, 
I have a sample code below, to show the problem, I am facing when using Sinon 
It is written using Typescript

    class IPC {
       
private publisher: redis.RedisClient;
        constructor
() {
           
this.publisher = redis.createClient();
       
}
   
        publish
(text: string) {
           
const msg = {
                text
: text
           
};
   
           
this.publisher.publish('hello', JSON.stringify(msg));
       
}
   
}


How can I stub the private variable `publisher` , inside this class?
so I could test the code as shown below

    it('should return text object', () => {
       
const ipc = sinon.createStubInstance(IPC);
        ipc
.publish('world!');
   
       
// this will throw error, because ipc.publisher is undefined
       
assert.deepStrictEqual({
            text
: 'world!'
       
}, ipc.publisher.getCall(0).args[0])
   
})


May I know how can I solve this problem? what would be the best solution?

Christian Johansen

unread,
Feb 22, 2017, 12:38:03 PM2/22/17
to Sinon.JS
Private properties are implementation details. Any use of them in your tests, including stubs, is bound to cause you headaches. If you want to test the interaction with the publisher, inject it in your constructor. Your IPC class is currently VERY tightly coupled to Redis.

C

--
You received this message because you are subscribed to the Google Groups "Sinon.JS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sinonjs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Swee Tim

unread,
Feb 23, 2017, 9:53:23 AM2/23/17
to Sinon.JS, chri...@cjohansen.no
I have changed my code to inject the dependency into the constructor

class IPC {    
    constructor
(private publisher: redis.RedisClient) {

   
}


    publish
(text: string) {
       
const msg = {
            text
: text
       
};


       
this.publisher.publish('hello', JSON.stringify(msg));
   
}
}



it
('should return text object', () => {
    sinon
.stub(redis, 'createClient')
       
.returns({
            publish
: sinon.spy()
       
});
   
const publisherStub = redis.createClient();


   
const ipc = new IPC(publisherStub)
    ipc
.publish('world!');


   
// this is working fine now
   
assert.deepStrictEqual({
        text
: 'world!'
   
}, publisherStub.publish.getCall(0).args[0])
 
    sinon
.restore(redis.createClient)
})


I would like to know, is this the right way for me to do stubbing?
and when I call the sinon.restore(redis.createClient) method, it will automatically restore the sinon.spy(), created for publish property?

Christian Johansen

unread,
Feb 24, 2017, 1:51:10 AM2/24/17
to Sinon.JS
Good for you! The sweet thing about injecting the dependency this way is that you don't need to involve the redis client at all:

const publisherStub = { publish: sinon.stub() };

Now you have defined a contract for your code that is independent of the specific dependency you will be using in production. Just beware that one drawback with this approach is that your test-code may assume things about the API that does not hold. This could be mitigated by e.g. comparing your stub instance to a real instance in another test, to make sure they have same methods etc. But don't worry about it too much - this is a general problem with stubbing in a dynamic language, and not specific to this approach.

Carl Erik Kopseng

unread,
Mar 19, 2017, 4:09:02 PM3/19/17
to Sinon.JS
> As I know, sinon dont stub properties, only method, 

that is actually not true. Sinon 2 can stub properties, but it still does not mean it is a good idea. the dependency injection you discussed with christian is a much better approach. still, if you would like to stub properties, you can do so using the `get` and `set` props of the stub object. See the docs.
Reply all
Reply to author
Forward
0 new messages