jquerytestingmocha.jssinonproxyquire

stub getJSON function of JQuery using proxyquire and sinon.js


I am trying to stub getJSON, done and fail function of jquery using sinon.js and proxyquire but always I am getting an error that .getJSON, done and fail is not function.

  const $ = require("jquery");
    const Progress = (() => {
      const getData = () => {
        return $.getJSON();
      };
      const showProgressBars = () => {
        getData()
          .done(function (data) {
            console.log(data);
          })
          .fail(function (e) {
            console.log("error getting data.", e);
          });
      };
      return {
          getData,
          showProgressBars
      }
    })();
module.exports=Progress;

Test

const proxyquire=require("proxyquire");
const sinon=require("sinon");
const expect=require("chai").expect;
describe("Progress", () => {
  it("should call getJSON", (done) => {
    const getJSONStub = sinon.stub();
    const jquerySpy = sinon.stub().callsFake(() => {
      return {
        getJSON: getJSONStub,
      
      };
    });
    const { getData } = proxyquire("../src/demo", {
      jquery: jquerySpy,
    });
    getData();
    expect(getJSONStub.callCount).to.be.eq(1);
    done();
  });
});

My requirement is to test these two functions but I am unable to do this. I already spent two days but unable to stub the getJSON, done and fail function.

Any help sould be highly appreciated


Solution

  • Use sinon.stub() to stub $.getJSON() method and its returned value JQuery.jqXHR which has .done() and .fail() callbacks. You can get the original callback passed in using .callsFake() method, after getting them, call these callbacks manually with mocked data or error.

    You don't need to use proxyquire package.

    E.g.

    index.js:

    const $ = require('jquery');
    
    const Progress = (() => {
      const getData = () => {
        return $.getJSON();
      };
      const showProgressBars = () => {
        getData()
          .done(function (data) {
            console.log(data);
          })
          .fail(function (e) {
            console.log('error getting data.', e);
          });
      };
      return {
        getData,
        showProgressBars,
      };
    })();
    
    module.exports = Progress;
    

    index.test.js:

    const sinon = require('sinon');
    const expect = require('chai').expect;
    const $ = require('jquery');
    const { getData, showProgressBars } = require('./');
    
    describe('Progress', () => {
      afterEach(() => {
        sinon.restore();
      });
      it('should call getJSON', async () => {
        const data = 'teresa teng';
        const getJSONStub = sinon.stub($, 'getJSON').resolves(data);
        const res = await getData();
        expect(res).to.be.equal('teresa teng');
        sinon.assert.calledOnce(getJSONStub);
      });
    
      it('should get data success', () => {
        const data = 'teresa teng';
        const mJqXHR = {
          done: sinon.stub().callsFake(function (callback) {
            callback(data);
            return this;
          }),
          fail: sinon.stub(),
        };
        const getJSONStub = sinon.stub($, 'getJSON').returns(mJqXHR);
        showProgressBars();
        sinon.assert.calledOnce(getJSONStub);
        sinon.assert.calledWithExactly(mJqXHR.done, sinon.match.func);
      });
    
      it('should handle error if get data fail', () => {
        const mErr = new Error('network');
        const mJqXHR = {
          done: sinon.stub().returnsThis(),
          fail: sinon.stub().callsFake(function (callback) {
            callback(mErr);
          }),
        };
        const getJSONStub = sinon.stub($, 'getJSON').returns(mJqXHR);
        showProgressBars();
        sinon.assert.calledOnce(getJSONStub);
        sinon.assert.calledWithExactly(mJqXHR.fail, sinon.match.func);
      });
    });
    

    unit test result:

      Progress
        ✓ should call getJSON
    teresa teng
        ✓ should get data success
    error getting data. Error: network
        at Context.<anonymous> (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/src/stackoverflow/67267452/index.test.js:34:18)
        at callFn (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runnable.js:364:21)
        at Test.Runnable.run (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runnable.js:352:5)
        at Runner.runTest (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:677:10)
        at /Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:801:12
        at next (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:594:14)
        at /Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:604:7
        at next (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:486:14)
        at Immediate._onImmediate (/Users/dulin/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:572:5)
        at processImmediate (internal/timers.js:461:21)
        ✓ should handle error if get data fail
    
    
      3 passing (12ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |     100 |      100 |     100 |     100 |                   
     index.js |     100 |      100 |     100 |     100 |                   
    ----------|---------|----------|---------|---------|-------------------