Nodejs: Testing with sinon and async/await - javascript

Having trouble getting this test to run using sinon and async/await. Here is an example of what I'm doing:
// in file funcs
async function funcA(id) {
let url = getRoute53() + id
return await funcB(url);
}
async function funcB(url) {
// empty function
}
And the test:
let funcs = require('./funcs');
...
// describe
let stubRoute53 = null;
let stubFuncB = null;
let route53 = 'https://sample-route53.com/'
let id = '1234'
let url = route53 + id;
beforeEach(() => {
stubRoute53 = sinon.stub(funcs, 'getRoute53').returns(route53);
stubFuncB = sinon.stub(funcs, 'funcB').resolves('Not interested in the output');
})
afterEach(() => {
stubRoute53.restore();
stubFuncB.restore();
})
it ('Should create a valid url and test to see if funcB was called with the correct args', async () => {
await funcs.funcA(id);
sinon.assert.calledWith(stubFuncB, url)
})
Via console.log I've verified that funcA is producing the correct URL, however, I'm getting the error AssertError: expected funcB to be called with arguments. When I try calling stubFuncB.getCall(0).args it prints out null. So maybe it is my lack of understanding of async/await, but I cannot figure out why the url is not being passed to that function call.
Thanks

I think your funcs declaration is not correct. Sinon could not stub getRoute53 and funcB called inside funcA Try this one:
funcs.js
const funcs = {
getRoute53: () => 'not important',
funcA: async (id) => {
let url = funcs.getRoute53() + id
return await funcs.funcB(url);
},
funcB: async () => null
}
module.exports = funcs
tests.js
describe('funcs', () => {
let sandbox = null;
beforeEach(() => {
sandbox = sinon.createSandbox();
})
afterEach(() => {
sandbox.restore()
})
it ('Should create a valid url and test to see if funcB was called with the correct args', async () => {
const stubRoute53 = sandbox.stub(funcs, 'getRoute53').returns('https://sample-route53.com/');
const stubFuncB = sandbox.stub(funcs, 'funcB').resolves('Not interested in the output');
await funcs.funcA('1234');
sinon.assert.calledWith(stubFuncB, 'https://sample-route53.com/1234')
})
})
P.S. Also, use sandbox. It's easier to clean stubs

Related

Using Jest with NodeJS, Function in imported module not being mocked without using 'this' in main

In the setup below, if I run the test as is, myFunc is not mocked when I debug into handler.
However, if instead I add this. in front of the myFunc call in handler, then the function is mocked and everything works as expected.
Can someone please explain why this is? I'm new to mocking and can't see it.
I know what this does, but why won't jest mock without it since I told it to mock that function in the module?
index.js
const aws = require('aws-sdk')
exports.handler = async function (event, context) {
let s;
switch (event.func) {
case "myFunc":
console.log('Executing myFunc');
//making the call: s = await this.myFunc.apply(null, [event.params]) will make the mock work.
s = await myFunc.apply(null, [event.params])
console.log(s);
return s;
/*cases...*/
default:
// default behaviour
}
async myFunc({p1, p2}){
/* do something */
return x
}
exports.myFunc = myFunc
}
index.spec.js
jest.mock('./index.js', () => {
const allAutoMocked = jest.createMockFromModule('./index.js')
const actual = jest.requireActual('./index.js')
return {
__esModules: true,
...allAutoMocked,
myFunc : jest.fn().mockImplementation(() => ({ mockedValue: 'test' })),
handler: actual.handler
}
})
let index = require("./index.js")
describe('Test myFunc', () => {
test('If myFunc function was called', async () => {
var event = { func: 'myFunc', params: { p1: xx, p2: false } };
const context = {};
const logMock = jest.fn((...args) => console.log(...args));
const data = await handler(event, context);
})
})

Jest mock not allowing inner function to be resolved in individual test

I'm testing a cloud function named myCloudFn in my functions/send.js module. My tests are in functions/test/send.test.js:
// send.js
const { getCompareDate } = require('../utils.js');
async function myCloudFn(myTestDate) {
const compareDate = await getCompareDate(argToTest);
const isOlder = myTestDate < compareDate;
return isOlder ? 'older' : 'newer';
}
module.exports = { myCloudFn };
// send.test.js
const send = require('../send.js');
jest.mock('../utils', () => ({
getCompareDate: jest.fn(() => new Date('2020-01-31')) // default
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02')),
}));
describe('send.js', () => {
it('returns date comparison from myCloudFn()', async () => {
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
});
The test functions correctly and passes as expected when I mock getCompareDate in this way, but for flexibility, I would rather provide custom input values for getCompareDate inside my tests and not 'globally'. Here's what I've tried:
const mockGetCompareDate = jest.fn();
jest.mock('../utils', () => ({
getCompareDate: mockGetCompareDate,
}));
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
This method, however, is not working and throws an error:
ReferenceError: Cannot access 'mockGetCompareDate' before initialization
I've used this method with other tests as noted in the solution in this question, but I am not seeing similar results here. What am I missing?
Jest is hoisting the mocked function to the top of the module, and hence throws this error. The mock should instead be used right before you run the test. Further reading.
Try this:
const { getCompareDate } = require('../utils.js');
const mockGetCompareDate = jest.fn(() => new Date('2020-01-31'));
jest.mock('../utils.js', () => ({
__esModule: true,
getCompareDate: jest.fn(),
default: jest.fn()
}));
beforeAll(() => {
getCompareDate.mockImplementation(mockGetCompareDate);
});
To provide custom values do as you did before, when initialising the mock function. Source
Like this:
const mockGetCompareDate = jest.fn()
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
Or do as you did before inside the test. Source
Like this:
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));

Problem running Jest using CLS with async functions

I can't seem to get CLS to work with Jest.
The following code:
export {}
const { promises: fs } = require('fs')
describe('CLS tests', () => {
test('Can test CLS', async () => {
var createNamespace = require('cls-hooked').createNamespace
var session = createNamespace('session')
session.run(async function () {
await fs.readFile('package.json', 'utf-8')
console.log('I cant log this')
})
})
})
Results in the following error:
Cannot log after tests are done. Did you forget to wait for something
async in your test?
Attempted to log "I cant log this".
Why is it that my test appears to be exiting early?
Maybe you need to abstract out the asynchronous operations. I tried this on my system and it works.
const {promises: fs} = require('fs')
const runSession = () => new Promise((resolve, reject) => {
const createNamespace = require('cls-hooked').createNamespace
const session = createNamespace('session')
session.run(() => {
fs.readFile('package.json', 'utf-8')
.then(resolve)
})
})
describe('CLS tests', () => {
test('Can test CLS', async () => {
const result = await runSession()
console.log('hello')
console.log(result)
expect(result).toBeTruthy()
console.log('after expect...')
})
})
Good Luck...

Unit testing an inner function?

I have a function that has inner functions, for my unit test, I only want to test the functionality of the inner function, but when I export the function and call the inner function, npm tests returns an error.
In my main.js:
mainFunction = () => {
functionToBeTested = () => {
// some code
}
}
module.exports = {mainFunction: mainFunction}
In my test.js
const chai = require("chai");
const assert = require("chai").assert;
const mainFunction = require("./main");
describe ("test", () => {
it("returns results", () => {
let result = mainfunction.functionToBeTested(args);
//equal code
});
})
But when I run npm test, it says:
mainfunction.functionToBeTested is not a function.
What am I doing wrong?
If you want to chain your functions you can try something like that.
main.js
const mainFunction = () => {
const functionToBeTested = () => {
return "I got it";
}
return { functionToBeTested };
}
module.exports = { mainFunction };
test.js
const chai = require("chai");
const assert = require("chai").assert;
const mainFunction = require("./main");
const mf = mainFunction();
describe ("test", () => {
it("returns results", () => {
let result = mf.functionToBeTested(args);
//equal code
});
});
Actually, you can't call a function declare inside another function that way. A solution would be to declare functionToBeTested outside mainFunction, then call it :
main.js
const functionToBeTested = () => {
// some code
};
const mainFunction = () => {
functionToBeTested();
};
module.exports = { mainFunction, functionToBeTested }
test.js
const chai = require("chai");
const assert = require("chai").assert;
const { mainFunction, functionToBeTested } = require("./main");
describe ("test", () => {
it("tests mainFunction", () => {
let main = mainfunction(args);
...
});
it("tests functionToBeTested"), () => {
let tested = functionToBeTested(args);
...
});
})
It is because only mainFunction() is exported and not the functionToBeTested(), outside this module JS doesn't knows about the existence of the functionToBeTested().
I will recommend you to move functionToBeTested separated and export that as well or have a helper method for calling it.

Successfully passed `this` context to an async function but how to retrieve `this` context when it is done NodeJS?

I am able to console log this in the context I want as long as I'm in the event listener. But I can't seem to update or change the more global object. How can I return the value to update a more global object?
I've tried to call the functions with bind, I also thought maybe it is an async problem and tried to solve using promises.
const { createReadStream } = require("fs");
const { createInterface } = require("readline");
export default class Process {
constructor() {
let self = this;
let file = [];
let lineReader = createInterface({
input: createReadStream(this.filePath)
});
lineReader
.on("line", line => {
file.push(line);
})
.on("close", () => {
self.file = file;
console.log(self);
return self;
});
}
console.log(this);
}
}
I expect the output to be:
Process {
filePath: 'path/to/file',
file:
[ 'line1',
'line2'
] }
The actual output is:
Process {
filePath: 'path/to/file' }
The answer is that the createInterface method returns a promise making this an async issue. This guide helped me implement a Promise to solve the issue. Also used this reference to help with syntax:
const { createReadStream } = require("fs");
const { createInterface } = require("readline");
function processFile(filePath) {
let data = '';
return new Promise((resolve, reject) => {
createInterface({
input: createReadStream(filePath)
})
.on("line", line => {
data += line + '\n';
})
.on("close", f => {
resolve(data)
})
})
}

Categories