Updated version
@dman, since you updated your test case, I owe you an updated answer. Although it is paraphrased, the script is still unorthodox - it seems you want to in some way ignore the "law of gravity", although you know it directly in front of you.
I will try to be as descriptive as possible. You have two functions that make async design by design. a () calls b () sequentially - by the way, this is not a recursion . Both functions do not notify their subscribers of completion / failure, i.e. They are considered fire-and-forget .
Now let's look at your test case. You create 3 stubs. Two of them allow a string and one combination of their execution using Promise.all() . Then you proxy the node-fetch module
proxy('./updated', { 'node-fetch': fetch });
using a stub that returns the combined execution of stubs 1 and 2. Now, if you print the allowed fetch value in any of the functions, you will see that instead of a string it is an array of stubs.
function a () { console.log('function a runs'); fetch('http://localhost') .then((e) => { console.log('first fetch', e); b(); }) .catch((e) => { console.log('error'); }); }
I think this is not a supposed conclusion. But let it move on, as it still does not kill your test. Then you added this statement along with the done () operator.
fetch.should.have.been.callCount(2); done();
The problem is that if you use done () or not, the effect will be exactly the same. You are running your script in sync mode. Of course, in this case the statement always fails. But itβs important to understand why.
So rewrite the script to mimic the nature of the async behavior you want to test.
'use strict'; const chai = require('chai'); const sinon = require('sinon'); const SinonChai = require('sinon-chai'); chai.use(SinonChai); chai.should(); const proxy = require('proxyquire'); describe('fetch test demo', () => { it('fetch should of called twice', (done) => { var fetchStub = sinon.stub(); var fetchStub2 = sinon.stub(); var fetch = sinon.stub(); fetchStub.returns(Promise.resolve('hello')); fetchStub2.returns(Promise.resolve('hi')); var promises = [fetchStub, fetchStub2]; fetch.returns(Promise.all(promises)); proxy('./updated', { 'node-fetch': fetch }); setTimeout(() => { fetch.should.have.been.callCount(2); done(); }, 10); }); });
As you can see, the only change was to complete the statement in the timer block. Nothing special - just wait 10 ms and then approve. Now the test passes as expected. Why?
Ok, for me it's pretty simple. You want to test 2 sequentially executed async functions and still run your statements in sync mode. It sounds cool, but it wonβt happen :) So, you have 2 options:
- You have features to notify callers when completed, and then run your claims in async mode
- Imitate the asynchronous nature of things using unorthodox methods.
Source test case response
It can be done. I reanalyzed your provided files so that it could be executed.
index.js
const fetch = require('node-fetch'); const sendAlert = require('./alerts').sendAlert; module.exports.init = function () { return new Promise((resolve, reject) => { fetch('https://localhost') .then(function () { sendAlert().then(() => { resolve(); }).catch( e => reject(e) ); }) .catch(e => { reject(e); }); }); };
alerts.js
const fetch = require('node-fetch'); module.exports.sendAlert = function () { return new Promise((resolve, reject) => { fetch('https://localhost') .then(function () { resolve(); }).catch((e) => { reject(e); }); }); };
test.js
'use strict'; const chai = require('chai'); const sinon = require('sinon'); const SinonChai = require('sinon-chai'); chai.use(SinonChai); chai.should(); const proxy = require('proxyquire'); describe.only('lifx alert test', () => { it('fetch should of called twice', (done) => { var body = { 'hourly': { data: [{ time: 1493413200, icon: 'clear-day', precipIntensity: 0, precipProbability: 0, ozone: 297.17 }] } }; var response = { json: () => { return body; } }; const fetchStub = sinon.stub(); fetchStub.returns(Promise.resolve(response)); fetchStub['@global'] = true; var stubs = { 'node-fetch': fetchStub }; const p1 = proxy('./index', stubs); p1.init().then(() => { try { fetchStub.should.have.been.calledTwice; done(); } catch (e) { done(e); } }).catch((e) => done(e)); }); });
What you are trying to do is albeit a little unorthodox when it comes to good unit testing methods. Although proxyquire supports this bypass mode through a feature called global overrides This is explained here why everyone should think twice before going down this path.
For your example to pass the test, you just need to add an additional attribute for the Sinon stub called @global , and set it to true. This flag overrides the require () caching mechanism and uses the provided stub regardless of which module is called.
So, although what you ask for can be done, I will have to agree with users who have commented on your question, that this should not be accepted as the right way to structure your tests.