Search code examples
javascriptunit-testingtestingmocha.jssinon

Javascript Mocha/Sinon fake XMLHttpRequest to return object in middle of tested method


Hello so i am new to using mocha/sinon and i have this issue

i am testing this method

export async function onConfigEvent(event: configEvent, otherConfig: otherConfig) {
  var myConfig = event.config as Config;

  const info = await fetchInfo(otherConfig.detailsPath, myConfig.Id);

  if (!isDefined(info)) {
    console.log('info is undefined.');
    return;
  }
}

this method is actually quite much bigger with plenty of if statements so i want to test every path of it. the problem i have is with the fetchInfo() call. this function will call a function that creates an url and so on and then calls another function the makes the actual call with a promise. what i need to to is to fake this fetchInfo and make it return what i want for this instance i want it to return undefined so it goes into the if and in other cases i want it to return a faked model like if it succeeded. but i have no idea on how to do it and i have searched for a long while and started trying with sinon.stub but how would i implement that here?

this is how my test looks so far

it('should log info undefined', () => {

let event: configEvent = {
  type: events.Config,
  config: { ["Id"]: 361 }
}

let fetch = sinon.stub(fetchResource);

let otherConfig = getOtherConfig();

let spy = sinon.spy(console, 'log');

onConfigEvent(event, otherConfig)

assert(spy.calledWith('info is undefined.'));

spy.restore();

});

where fetchResource is the function that fetchInfo calls but this does nothing


Solution

  • You need to stub fetchInfo function. sinon.js doesn't support stub a standalone function directly. Need to use rewire package.

    E.g.

    index.ts:

    function isDefined(obj) {
      return typeof obj !== 'undefined';
    }
    
    export async function fetchInfo(url, id) {
      //
    }
    
    export async function onConfigEvent(event, otherConfig) {
      const myConfig = event.config;
    
      const info = await fetchInfo(otherConfig.detailsPath, myConfig.Id);
    
      if (!isDefined(info)) {
        console.log('info is undefined.');
        return;
      }
    }
    

    index.test.ts:

    import rewire from 'rewire';
    import sinon from 'sinon';
    
    describe('60828989', () => {
      afterEach(() => {
        sinon.restore();
      });
      it('should print log if info is undefined', async () => {
        const mod = rewire('./');
        const fetchInfoStub = sinon.stub().resolves();
        const logSpy = sinon.spy(console, 'log');
        mod.__set__('fetchInfo', fetchInfoStub);
        const event = { config: { id: 1 } };
        const otherConfig = { detailsPath: 'http:/localhost' };
        await mod.onConfigEvent(event, otherConfig);
        sinon.assert.calledWithExactly(logSpy, 'info is undefined.');
      });
      it('should do nothing if info is defined', async () => {
        const mod = rewire('./');
        const fetchInfoStub = sinon.stub().resolves({});
        const logSpy = sinon.spy(console, 'log');
        mod.__set__('fetchInfo', fetchInfoStub);
        const event = { config: { id: 1 } };
        const otherConfig = { detailsPath: 'http:/localhost' };
        await mod.onConfigEvent(event, otherConfig);
        sinon.assert.notCalled(logSpy);
      });
    });
    

    unit test results with coverage report:

      60828989
    info is undefined.
        ✓ should print log if info is undefined (124ms)
        ✓ should do nothing if info is defined
    
    
      2 passing (150ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |     100 |      100 |   66.67 |     100 |                   
     index.ts |     100 |      100 |   66.67 |     100 |                   
    ----------|---------|----------|---------|---------|-------------------
    

    source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60828989