Search code examples
javascriptnode.jsclosuressinonspy

How to spy a function with Sinon.js that is in the same js file as the function under test


I have a problem with Sinon.js while trying to spy a function that is in the same javascript file as the function that I want to test. Furthermore I assert that the spied function is called once. Unfortunately the test fails. Interesting thing is, that if the spied function is in another javascript file than the function under test it works !

Here is my code:

mock_test.js:

   
var sinon = require('sinon')

var one = require('./one.js')
var two = require('./two.js')

describe('Spy ', function () {

  it('Spy another method', sinon.test(function (done) {
    var another_method_spy = sinon.spy(one, 'another_method')

    one.some_method()
    sinon.assert.calledOnce(another_method_spy)
    done()
  }))

  it('Spy second method', sinon.test(function (done) {
    var second_method = sinon.spy(two, 'second')

    one.call_second()
    sinon.assert.calledOnce(second_method)
    done()
  }))

})

one.js:

var two = require('./two.js')

var some_method = function(){
  console.log('one: some method')
  another_method()
}

var another_method = function(){
  console.log('one: another method')
}

var call_second = function(){
  console.log('one: call second')
  two.second()
}

module.exports.some_method = some_method
module.exports.another_method = another_method
module.exports.call_second = call_second

two.js:

var second = function(){
  console.log('two: second')
}

module.exports.second = second

I couldn't find anything helpful in the internet and also I tried different things. Please help, what am I missing here ?

Cheers Noah


Solution

  • Unfortunately the test fails

    That is because one.some_method() in mock_test.js invokes another_method inside the closure one.some_method() is holding over the content of one.js and not one.another_method in mock_test.js.

    Illustration by example

    Lets re-write one.js to:

    var a = 'I am exported';
    var b = 'I am not exported';
    
    function foo () {
        console.log(a);
        console.log(this.b)
    }
    
    module.exports.a=a;
    module.exports.foo=foo;
    

    and mock_test.js to:

    var one = require('./one');
    
    console.log(one); // { a: 'I am exported', foo: [Function: foo] }
    
    one.a = 'Charles';
    one.b = 'Diana';
    
    console.log(one); // { a: 'Charles', foo: [Function: foo], b: 'Diana' }
    

    Now if we invoke one.foo() it will result in:

    I am exported
    Diana
    

    The I am exported is logged to the console because console.log(a) inside foo points to var a inside the closure foo is holding over the contents of one.js.

    The Diana is logged to the console because console.log(this.b) inside foo points to one.b in mock_test.js.

    So what do you need to do to make it work?

    You need to change:

    var some_method = function(){
      console.log('one: some method')
      another_method()
    }
    

    to:

    var some_method = function(){
      console.log('one: some method')
      this.another_method()
    }