How to claim that a slice sample more than once

Using proxyquire, sinon and mocha.

I can get a stub on the first call to the fetch. But on the second call to fetch, which is recursive, I cannot assert it. It can be seen from the conclusion that the statement can be executed before the test is completed. You will see this using the second fetch console after approval.

index.js

 var fetch = require('node-fetch'); function a() { console.log('function a runs'); fetch('https://www.google.com') .then((e) => { console.log('first fetch'); b(); }) .catch((e)=> { console.log('error') }); } function b() { fetch('https://www.google.com') .then((e) => { console.log('second fetch'); }) .catch((e)=> { console.log('error') }); } a() 

test:

 describe('fetch test demo', ()=> { it('fetch should of called twice', (done)=> { fetchStub = sinon.stub(); fetchStub2 = sinon.stub(); fetch = sinon.stub(); fetchStub.returns(Promise.resolve('hello')); fetchStub2.returns(Promise.resolve('hi')); var promises = [ fetchStub, fetchStub2 ] fetch.returns(Promise.all(promises)); proxy('../index', { 'node-fetch': fetch }); fetch.should.have.been.callCount(2); done() }); }); 

  fetch test demo function a runs 1) fetch should of called twice first fetch second fetch lifx alert test - fetch should of called three times when rain change is over 50% - should run fetch twice 0 passing (78ms) 2 pending 1 failing 1) fetch test demo fetch should of called twice: expected stub to have been called exactly twice, but it was called once stub(https://www.google.com) => [Promise] { } at a (/home/one/github/lifx-weather/foobar.js:5:3) AssertionError: expected stub to have been called exactly twice, but it was called once stub(https://www.google.com) => [Promise] { } at a (foobar.js:5:3) at Context.it (test/bar.js:22:28) 
+7
javascript unit-testing sinon proxyquire
source share
3 answers

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.

+3
source share

Here is an alternative way to do this using Promise.all() .

Note: this will not work if you use the fetch json method and you need to pass the data to resolve() for the data logic. It will only go through stubs when resolved. However, it will indicate the number of times.

 describe('fetch test demo', () => { it('fetch should of called twice', () => { let fetchStub = sinon.stub(); let fetchStub2 = sinon.stub(); let fetch = sinon.stub(); fetchStub.returns(Promise.resolve('hello')); fetchStub2.returns(Promise.resolve('hi')); var promises = [ fetchStub, fetchStub2 ] var promise = Promise.all(promises); fetch.returns(promise); proxy('../foobar', { 'node-fetch': fetch }); return promise.then(() => { fetch.should.have.callCount(2); }); }); }); 
0
source share

I found another way to get things done. Maybe this might work for someone.

  describe('Parent', () => { let array: any = []; before(async () => { array = await someAsyncDataFetchFunction(); asyncTests(); }); it('Dummy test to run before()',async () => { expect(0).to.equal(0); // You can use this test to getting confirm whether data fetch is completed or not. }); function asyncTests() { array.forEach((currentValue: any) => { describe('Child', async () => { it('Test '+ currentValue ,() => { expect(currentValue).to.equal(true); }) }) }); } }); 

This is how I got the statement on each element of the array. (Data from the array is retrieved asynchronously).

0
source share

All Articles