I have a major function that call 3 other functions. I want to test if those 3 function were called.
import axios from 'axios';
export async function functionOne() {
console.log('Hit functionOne');
const result = await axios.get('https://www.urlOne.com/first');
return result;
}
export async function functionTwo() {
const result = await axios.get('https://www.urlTwo.com/second');
return result;
}
export async function functionThree() {
const result = await axios.get('https://www.urlThree.com/third');
return result;
}
export async function mainFunction() {
const resultOne = await functionOne();
// some logic that also calls functionTwo & functionThree
}
import * as myCtrl from '../controllers';
import { expect } from 'chai';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import * as chaiAsPromised from 'chai-as-promised';
chai.use(sinonChai);
chai.use(chaiAsPromised);
chai.should();
describe('success', () => {
const sandbox = sinon.createSandbox();
// Create Spies
beforeEach(() => {
sandbox.spy(myCtrl, 'functionOne');
sandbox.spy(myCtrl, 'functionTwo');
sandbox.spy(myCtrl, 'functionThree');
});
afterEach(() => {
sandbox.restore();
})
it('test something', async () => {
nock('https://www.urlOne.com')
.get('first')
.reply(200)
nock('https://www.urlTwo.com')
.get('second')
.reply(200)
nock('https://www.urlThree.com')
.get('third')
.reply(200)
await myCtrl.mainFunction();
expect(myCtrl.functionOne).to.be.calledOnce();
expect(myCtrl.functionTwo).to.be.calledOnce();
expect(myCtrl.functionThree).to.be.calledOnce();
});
});
It looks weird, but according to the nock documentation, using multiple nocks is the right way.
Also tried using axios-mock-adapter instead of Nock, but the same error occurs.
The problem is, it always returns:
AssertionError: expected functionOne to have been called exactly once, but it was called 0 times
I can see the console.log
message on console, but the count still 0. Am I missing something here?
It's an import scope issue.
Stubbing or spying a module depends on export style. The mentioned case imports separate exports and assemble them into one.
import * as myCtrl from '../controllers';
The problem is that functionOne,two,three
in mainFunction()
are different from the ones that you've imported and spied. sinon
can't expose and modify the function's inner scope. It can only modify what it can access.
// for convenience, let's say
// functionOne, functionTwo, functionThree = f1,f2,f3
// controller.js
export function f1() {}
export function f2() {}
export function f3() {}
export function mainFunction() { f1(); f2(); f3() }
// spying(stubbing) in test
import { f1, f2, f3, mainFunction } from './controller';
// it does not wrap or modify mainFunction.f1()
// because,
// f1 !== mainFunction.f1
sinon.spy(f1)
// get the functions as arguments, so that you can test the pure logic
// controller.js
export function f1() {}
export function f2() {}
export function f3() {}
export function mainFunction(f1,f2,f3) { f1(); f2(); f3(); }
// test
import { mainFunction } from 'controller';
describe('', () => {
let f1;
// ...
before(() => {
// ...
f1 = sinon.spy(require('controller').f1);
})
it('test', () => {
mainFunction(f1, f2, f3);
// check the spy from here
})
})
separate functions into different modules then stub a dependency of the module
make the module as a whole, and modify the specific property.
// controller
// it does not have to be a class. just make the inner functions to share the same scope.
class Controller {
constructor() {}
f1() {}
f2() {}
f3() {}
mainFunction() {
this.f1(); // ...implement logic here
}
}
// test
import Controller from 'controller';
describe('', () => {
let controller;
before(() => {
controller = new Controller();
sinon.spy(controller.f1);
})
it('test', () => {
controller.mainFunction()
// check the spy from here
})
})