Mock Node.js stream pipeline using Jest - node.js

I have a method where I am using Node.js pipeline() that is having a last argument for a callback.
doSomething(readStream, destinationwritableStream): void {
pipeline(readStream, destinationwritableStream, async () => {
// some code here.
// unable to cover this code using JEST
})
}
Any suggestions, how I can cover this async() callback or how to mock pipeline() method.
Thanks in advance.

you can mock it like that:
jest.mock('./../path/to/file/that/includes/pipeline');
const { pipeline } = require('./../path/to/file/that/includes/pipeline');
const pipelineMock = async () => {
return true;
};
pipeline.mockImplementation(pipelineMock);

Related

NodeJs async SyntaxError [duplicate]

I wrote this code in lib/helper.js:
var myfunction = async function(x,y) {
....
return [variableA, variableB]
}
exports.myfunction = myfunction;
Then I tried to use it in another file :
var helper = require('./helper.js');
var start = function(a,b){
....
const result = await helper.myfunction('test','test');
}
exports.start = start;
I got an error:
await is only valid in async function
What is the issue?
The error is not refering to myfunction but to start.
async function start() {
....
const result = await helper.myfunction('test', 'test');
}
// My function
const myfunction = async function(x, y) {
return [
x,
y,
];
}
// Start function
const start = async function(a, b) {
const result = await myfunction('test', 'test');
console.log(result);
}
// Call start
start();
I use the opportunity of this question to advise you about an known anti pattern using await which is : return await.
WRONG
async function myfunction() {
console.log('Inside of myfunction');
}
// Here we wait for the myfunction to finish
// and then returns a promise that'll be waited for aswell
// It's useless to wait the myfunction to finish before to return
// we can simply returns a promise that will be resolved later
// useless async here
async function start() {
// useless await here
return await myfunction();
}
// Call start
(async() => {
console.log('before start');
await start();
console.log('after start');
})();
CORRECT
async function myfunction() {
console.log('Inside of myfunction');
}
// Here we wait for the myfunction to finish
// and then returns a promise that'll be waited for aswell
// It's useless to wait the myfunction to finish before to return
// we can simply returns a promise that will be resolved later
// Also point that we don't use async keyword on the function because
// we can simply returns the promise returned by myfunction
function start() {
return myfunction();
}
// Call start
(async() => {
console.log('before start');
await start();
console.log('after start');
})();
Also, know that there is a special case where return await is correct and important : (using try/catch)
Are there performance concerns with `return await`?
To use await, its executing context needs to be async in nature
As it said, you need to define the nature of your executing context where you are willing to await a task before anything.
Just put async before the fn declaration in which your async task will execute.
var start = async function(a, b) {
// Your async task will execute with await
await foo()
console.log('I will execute after foo get either resolved/rejected')
}
Explanation:
In your question, you are importing a method which is asynchronous in nature and will execute in parallel. But where you are trying to execute that async method is inside a different execution context which you need to define async to use await.
var helper = require('./helper.js');
var start = async function(a,b){
....
const result = await helper.myfunction('test','test');
}
exports.start = start;
Wondering what's going under the hood
await consumes promise/future / task-returning methods/functions and async marks a method/function as capable of using await.
Also if you are familiar with promises, await is actually doing the same process of promise/resolve. Creating a chain of promise and executes your next task in resolve callback.
For more info you can refer to MDN DOCS.
When I got this error, it turned out I had a call to the map function inside my "async" function, so this error message was actually referring to the map function not being marked as "async". I got around this issue by taking the "await" call out of the map function and coming up with some other way of getting the expected behavior.
var myfunction = async function(x,y) {
....
someArray.map(someVariable => { // <- This was the function giving the error
return await someFunction(someVariable);
});
}
I had the same problem and the following block of code was giving the same error message:
repositories.forEach( repo => {
const commits = await getCommits(repo);
displayCommit(commits);
});
The problem is that the method getCommits() was async but I was passing it the argument repo which was also produced by a Promise. So, I had to add the word async to it like this: async(repo) and it started working:
repositories.forEach( async(repo) => {
const commits = await getCommits(repo);
displayCommit(commits);
});
If you are writing a Chrome Extension and you get this error for your code at root, you can fix it using the following "workaround":
async function run() {
// Your async code here
const beers = await fetch("https://api.punkapi.com/v2/beers");
}
run();
Basically you have to wrap your async code in an async function and then call the function without awaiting it.
The current implementation of async / await only supports the await keyword inside of async functions Change your start function signature so you can use await inside start.
var start = async function(a, b) {
}
For those interested, the proposal for top-level await is currently in Stage 2: https://github.com/tc39/proposal-top-level-await
async/await is the mechanism of handling promise, two ways we can do it
functionWhichReturnsPromise()
.then(result => {
console.log(result);
})
.cathc(err => {
console.log(result);
});
or we can use await to wait for the promise to full-filed it first, which means either it is rejected or resolved.
Now if we want to use await (waiting for a promise to fulfil) inside a function, it's mandatory that the container function must be an async function because we are waiting for a promise to fulfiled asynchronously || make sense right?.
async function getRecipesAw(){
const IDs = await getIds; // returns promise
const recipe = await getRecipe(IDs[2]); // returns promise
return recipe; // returning a promise
}
getRecipesAw().then(result=>{
console.log(result);
}).catch(error=>{
console.log(error);
});
If you have called async function inside foreach update it to for loop
Found the code below in this nice article: HTTP requests in Node using Axios
const axios = require('axios')
const getBreeds = async () => {
try {
return await axios.get('https://dog.ceo/api/breeds/list/all')
} catch (error) {
console.error(error)
}
}
const countBreeds = async () => {
const breeds = await getBreeds()
if (breeds.data.message) {
console.log(`Got ${Object.entries(breeds.data.message).length} breeds`)
}
}
countBreeds()
Or using Promise:
const axios = require('axios')
const getBreeds = () => {
try {
return axios.get('https://dog.ceo/api/breeds/list/all')
} catch (error) {
console.error(error)
}
}
const countBreeds = async () => {
const breeds = getBreeds()
.then(response => {
if (response.data.message) {
console.log(
`Got ${Object.entries(response.data.message).length} breeds`
)
}
})
.catch(error => {
console.log(error)
})
}
countBreeds()
In later nodejs (>=14), top await is allowed with { "type": "module" } specified in package.json or with file extension .mjs.
https://www.stefanjudis.com/today-i-learned/top-level-await-is-available-in-node-js-modules/
This in one file works..
Looks like await only is applied to the local function which has to be async..
I also am struggling now with a more complex structure and in between different files. That's why I made this small test code.
edit: i forgot to say that I'm working with node.js.. sry. I don't have a clear question. Just thought it could be helpful with the discussion..
function helper(callback){
function doA(){
var array = ["a ","b ","c "];
var alphabet = "";
return new Promise(function (resolve, reject) {
array.forEach(function(key,index){
alphabet += key;
if (index == array.length - 1){
resolve(alphabet);
};
});
});
};
function doB(){
var a = "well done!";
return a;
};
async function make() {
var alphabet = await doA();
var appreciate = doB();
callback(alphabet+appreciate);
};
make();
};
helper(function(message){
console.log(message);
});
A common problem in Express:
The warning can refer to the function, or where you call it.
Express items tend to look like this:
app.post('/foo', ensureLoggedIn("/join"), (req, res) => {
const facts = await db.lookup(something)
res.redirect('/')
})
Notice the => arrow function syntax for the function.
The problem is NOT actually in the db.lookup call, but right here in the Express item.
Needs to be:
app.post('/foo', ensureLoggedIn("/join"), async function (req, res) {
const facts = await db.lookup(something)
res.redirect('/')
})
Basically, nix the => and add async function .
"await is only valid in async function"
But why? 'await' explicitly turns an async call into a synchronous call, and therefore the caller cannot be async (or asyncable) - at least, not because of the call being made at 'await'.
Yes, await / async was a great concept, but the implementation is completely broken.
For whatever reason, the await keyword has been implemented such that it can only be used within an async method. This is in fact a bug, though you will not see it referred to as such anywhere but right here. The fix for this bug would be to implement the await keyword such that it can only be used TO CALL an async function, regardless of whether the calling function is itself synchronous or asynchronous.
Due to this bug, if you use await to call a real asynchronous function somewhere in your code, then ALL of your functions must be marked as async and ALL of your function calls must use await.
This essentially means that you must add the overhead of promises to all of the functions in your entire application, most of which are not and never will be asynchronous.
If you actually think about it, using await in a function should require the function containing the await keyword TO NOT BE ASYNC - this is because the await keyword is going to pause processing in the function where the await keyword is found. If processing in that function is paused, then it is definitely NOT asynchronous.
So, to the developers of javascript and ECMAScript - please fix the await/async implementation as follows...
await can only be used to CALL async functions.
await can appear in any kind of function, synchronous or asynchronous.
Change the error message from "await is only valid in async function" to "await can only be used to call async functions".

How to mock the return value of messages.create() method from twilio-node using sinon js/loopback testlab?

I'm trying to mock the return value of messages.create() method from twilio-node library.
Since the create method resides inside the interface called messages, i can't directly mock the return value of create method.
My Unit test:
import {
createStubInstance,
StubbedInstanceWithSinonAccessor,
} from '#loopback/testlab';
import sinon from 'sinon';
import {Twilio} from '../../../../clients/whatsapp-sms-clients/twilio.whatsapp-sms-clients';
import twilio from 'twilio';
describe('Twilio client (UnitTest)', () => {
let twilioMock: StubbedInstanceWithSinonAccessor<twilio.Twilio>;
let logger: StubbedInstanceWithSinonAccessor<LoggingService>;
let twilioClient: Twilio;
beforeEach(() => {
twilioMock = createStubInstance(twilio.Twilio);
logger = createStubInstance(LoggingService);
twilioClient = new Twilio(twilioMock, logger);
});
it('should create the message', async () => {
twilioMock.stubs.messages.create.resolves({
// mocked value
});
});
});
Thanks in advance.
Twilio developer evangelist here.
I've not worked with testlab/sinon like this before, but I think I have an idea of what you need to do, if not the right syntax.
You'd need to stub the response to twilioMock.messages to return an object that has a create property that is a stubbed function that resolves to the result you want. Something like this might work, or at least set you on the right track:
it('should create the message', async () => {
// Create stub for response to create method:
const createStub = sinon.stub().resolves({
// mocked value
});
// Stub the value "messages" to return an object that has a create property with the above stub:
twilioMock.stubs.messages.value({
create: createStub
});
// Rest of the test script
});
Edit
OK, using value above didn't work. I tried again. This version strips out your custom Twilio wrapper from the example and just calls things directly on the Twilio client stub itself. Hopefully you can use this as inspiration to work it into your tests.
What I realised is that twilioClient.messages is a getter and is dynamically defined. So, I directly stubbed the result on the stub client.
import {
createStubInstance,
StubbedInstanceWithSinonAccessor,
} from "#loopback/testlab";
import sinon from "sinon";
import { Twilio } from "twilio";
describe("Twilio client (UnitTest)", () => {
let twilioMock: StubbedInstanceWithSinonAccessor<Twilio>;
beforeEach(() => {
twilioMock = createStubInstance(Twilio);
});
it("should create the message", async () => {
const createStub = sinon.stub().resolves({
sid: "SM1234567",
});
sinon.stub(twilioMock, "messages").get(() => ({
create: createStub,
}));
const message = await twilioMock.messages.create({
to: "blah",
from: "blah",
body: "hello",
});
expect(message.sid).toEqual("SM1234567");
});
});
The above test passes for me in my setup.

Proper Jest Testing Azure Functions

I am wondering how to properly test Azure Functions with Jest. I have read the online documentation provided by MSoft but it's very vague, and brief. There are also some outdated articles I found that don't really explain much. Here is what I understand: I understand how to test normal JS async functions with Jest. And I understand how to test very simple Azure Functions. However I am not sure how to go about properly testing more complex Azure Functions that make multiple API calls, etc.
For example I have an HTTP Function that is supposed to make a few API calls and mutate the data and then return the output. How do I properly mock the API calls in the test? We only have one point of entry for the function. (Meaning one function that is exported module.exports = async function(context,req). So all of our tests enter through there. If I have sub functions making calls I can't access them from the test. So is there some clever way of mocking the API calls? (since actually calling API's during tests is bad practice/design)
Here is a sample of code to show what I mean
module.exports = async function (context, req)
{
let response = {}
if (req.body && req.body.id)
{
try
{
//get order details
response = await getOrder(context, req)
}
catch (err)
{
response = await catchError(context, err);
}
}
else
{
response.status = 400
response.message = 'Missing Payload'
}
//respond
context.res =
{
headers: { 'Content-Type': 'application/json' },
status: response.status,
body: response
}
};
async function getOrder(context, req)
{
//connection to db
let db = await getDb() // <- how to mock this
//retrieve resource
let item = await db.get...(id:req.body.id)... // <- and this
//return
return {'status':200, 'data':item}
}
Consider this (simplified) example.
src/index.js (Azure Function entry point):
const { getInstance } = require('./db')
module.exports = async function (context) {
// assuming we want to mock getInstance and db.getOrder
const db = await getInstance()
const order = await db.getOrder()
return order
}
src/db.js:
let db
async function getInstance() {
if (db === undefined) {
// connect ...
db = new Database()
}
return db
}
class Database {
async getOrder() {
return 'result from real call'
}
}
module.exports = {
getInstance,
Database,
}
src/__tests__/index.test.js:
const handler = require('../index')
const db = require('../db')
jest.mock('../db')
describe('azure function handler', () => {
it('should call mocked getOrder', async () => {
const dbInstanceMock = new db.Database() // db.Database is already auto-mocked
dbInstanceMock.getOrder.mockResolvedValue('result from mock call')
db.getInstance.mockResolvedValue(dbInstanceMock)
const fakeAzureContext = {} // fake the context accordingly so that it triggers "getOrder" in the handler
const res = await handler(fakeAzureContext)
expect(db.getInstance).toHaveBeenCalledTimes(1)
expect(dbInstanceMock.getOrder).toHaveBeenCalledTimes(1)
expect(res).toEqual('result from mock call')
})
})
> jest --runInBand --verbose
PASS src/__tests__/index.test.js
azure function handler
✓ should call mocked getOrder (4 ms)
For a complete quickstart, you may want to check my blog post

Unmock function after mockimplementation

I'm having a bit of trouble unmocking a function.
I first mock it and now I can't unmock it
//myClass.js
class myClass {
static check(v1,v2) {
return v1 > v2;
}
static async getinfo(v1,v2) {
if (this.check(v1,v2)) {
return await get('api.google.com');
}
return [];
}
}
//myclass.spec.js
describe('Testing myClass', () => {
describe('testing processing', () => {
it('should return result', () => {
const mockPatch = jest.fn().mockImplementation((version, solution) => false);
myClass.check = mockCheck;
try {
const result = await myClass.getinfo(1,2);
expect(result).toBe.([]);
}catch(e) {
throw e;
}
})
})
describe('Testing check', () => {
it('should return true', () => {
expect(myClass.check(2,1)).toBe.true
})
})
})
I already try with
myClass.check.mockRestore()
beforeEach(() => {myClass.check.mockRestore()})
jest.unmock('./myClass.js)
Is there anyway I can solve this? I read all the jest doc and i couldn't find anything
Methods should never be mocked by reassigning them, there is no way how Jest could restore their original implementation this way.
This should always be done with spyOn:
jest.spyOn(myClass, 'check').mockReturnValue(false)
This way a method can be restored with restoreMock or restoreAllMocks. This should be preferably enabled globally in Jest configuration.
I'm assuming that what you're hoping to do is to mock an implementation for use in a specific test, but then have your other tests function without the mocking.
If so, I think you could use the module mocking strategy in conjunction with mockReturnValueOnce.
Be sure to import your module at the top of your tests, then to call jest.mock with the same path. After that, you should be able to call myClass.check.mockReturnValueOnce, and it will be mocked until the next time it is called. After that, it will function normally 👍

Using jest spyOn cannot detect methods being called inside try-catch block

The problem I'm having is that Jest is reporting setResultsSpy is being called 0 times when in fact, I know that it is being called. I know this by putting console.log(results) under the const results = await getFileList(data.path); in my code and was able to see results returned.
My guess right now is that try-catch blocks creates a local scope, which is causing those calls to not be registered. If this is true, my question is "how can I test if those methods have been called"?
// test_myFunction.js
test((`myFunction with valid path should return list of files`), () => {
const actions = {
setMsg: () => { },
setButton: () => {},
setResults: () => {},
setAppState: () => {}
};
const setMsgSpy = jest.spyOn(actions, 'setMsg');
const setSubmitButtonStateSpy = jest.spyOn(actions, 'setButton');
const setResultsSpy = jest.spyOn(actions, 'setResults');
const setAppStateSpy = jest.spyOn(actions, 'setAppState');
const returnedFileList = [
'file1.pdf',
'file2.pdf',
'file3.pdf',
];
const requestConfig = {
component: COMPONENTS.myComponent,
request: RequestTypes.REQUEST,
data: {path: 'folder1'},
actions
};
processRequest(requestConfig)
expect(setMsgSpy).toHaveBeenCalledTimes(1);
expect(setMsgSpy)
.toHaveBeenCalledWith('loading');
expect(setButtonSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledWith(returnedFileList);
expect(setAppStateSpy).toHaveBeenCalledTimes(1);
expect(setAppStateSpy).toHaveBeenCalledWith('confirm');
});
_
// myFunction.js
async function processRequest({
component,
request,
data,
actions,
}){
if (component === COMPONENTS.myComponent) {
const path = data.path.trim();
switch (request) {
case RequestTypes.REQUEST:
actions.setMsg('message');
actions.setButton('disabled');
try {
const results = await getFileList(data.path);
actions.setResults(results);
actions.setAppState('confirm');
} catch (e) {
actions.setError(e);
actions.setAppState('error');
}
}
break;
default:
break;
}
}
The the problem was Jest was failing out of the test before the results from getFileList() execution has completed since getFileList() is an async function.
The solution is for the test to handle the execution asynchronously as per the documentation. There are 4 ways to solve this problem:
Use callbacks
Use .then() and .catch() on the returned promise (see docs on .then() here and .catch() here)
Use .resolves() or .rejects() Jest methods on expect() to let Jest resolve the promise.
Use Async-Await syntax by declaring the test anonymous function as async and using await on processRequest() .
I went with option 4 as I enjoy using async-await syntax. Here's the solution:
// test_myFunction.js
test((`myFunction with valid path should return list of files`), async () => {
//(all of the variables established from above)
await processRequest(requestConfig)
expect(setMsgSpy).toHaveBeenCalledTimes(1);
expect(setMsgSpy)
.toHaveBeenCalledWith('loading');
expect(setButtonSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledWith(returnedFileList);
expect(setAppStateSpy).toHaveBeenCalledTimes(1);
expect(setAppStateSpy).toHaveBeenCalledWith('confirm');
});
Notice async being used on the first line and await when calling processRequest(requestConfig).

Resources