sinon: how to mock a function that is returned from another function

Viewed 743

I'm trying to work with simple-git. I need to write some unit tests, and to do so I need to mock out some functions using sinon. The problem I'm having is that my mocks don't propogate into my file under test.

For example, in the file under test, I have this:

const git = require('simple-git/promise')
function func () {
    var promise
    if (repo_exists()) {
        promise = git().silent(true).clone('http://github.com/me/my-repo.git')
    } else {
        promise = git('my-repo').silent(true).pull('origin','master')
    }

    promise.then(() => {
        // do more stuff
    })
}

In my test file, I've tried this:

const git = require('simple-git/promise')()
sinon.stub(git, 'silent').callsFake(() => {
  return {
    clone: () => {
      console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~')
      console.log('calling clone')
      console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~')
      return new Promise((resolve, reject) => {
        console.log('clone')
        resolve()
      })
    },
    pull: () => {
      console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~')
      console.log('calling pull')
      console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~')
      return new Promise((resolve, reject) => {
        console.log('pull')
        resolve()
      })
    }
  }
})

But the mocked functions don't get called. I assume the reason is that require('simple-git/promise') returns a function, which itself returns the object containing functions I want to mock, but I don't know how to deal with this.

1 Answers

You're right, when git() is called it returns a new object each time. Methods of this object are eventually proxied to an instance of Git (https://github.com/steveukx/git-js/blob/master/src/git.js)

As an option, you can stub internal _run method of Git.prototype (the method responsible for scheduling the command for execution):

const Git = require('simple-git/src/git');

sinon.stub(Git.prototype, '_run').callsFake(function (command, cb) {
  console.log('called command', command)

  // to indicate success (will resolve eventual promise)
  cb.call(this, null, 'any message');

  // OR to indicate failure (will reject eventual promise)
  Git.fail(this, 'error message', cb);

  return this;
});

NB, non-arrow function under callsFake is essential to preserve this and return this is needed to comply with original behavior (https://github.com/steveukx/git-js/blob/master/src/git.js#L1271).

Related