How to resolve promise from a mocked callback function in jest - javascript

I am new to jest and trying to get some understanding of how mocks work. I am not able to figure out how I resolve an actual promise from a mocked module function.
I have a simple data interface module as shown below
const fs = require('fs');
const path = require('path');
function loadData(fileName){
let promise = new Promise((resolve, reject) => {
fs.readFile(path.join(__dirname, `/${fileName}`), (err, data) => {
if (err) {
console.error(err)
reject(err);
}
data = data.toString();
try{
data = JSON.parse(data);
}
catch(err){
console.error(err)
reject(new Error('Error while parsing json'))
}
resolve(data);
})
})
return promise;
}
The test file for the module looks like
jest.mock('fs');
const fs = require('fs');
const DI = require('./../data/data_interface');
describe('data loading functionality',() => {
test('load data opens file system',async () => {
try {
expect.assertions(1);
let fileName = 'expenses.json';
await DI.loadData(fileName);
expect(fs.readFile.mock.calls.length).toBe(1);
}
catch(err){
throw(err);
}
})
})
When I run the above test I am getting a timeout error which seems because the mock is not able to resolve the promise.
How do I resolve a promise from the provided mock?

You should remove the async await in your test

Related

TypeError: resolve is not a function

I am trying to write a function that will read a file and depending upon success or failure in reading it will call the resolve or reject function. Below is the code that I have written.
let fs = require('fs');
const FILE_NAME = './assets/pies.json';
let pieRepo = {
get: function(resolve, reject) {
fs.readFile(FILE_NAME, function(err, data) {
if(err) {
reject(err);
}
else {
resolve(JSON.parse(data));
}
});
}
};
module.exports = pieRepo;
However when I run "npm start" it throws the below error
/workingdirectory/repos/pieRepo.js:12
resolve(JSON.parse(data));
^
TypeError: resolve is not a function
at /workingdirectory/repos/pieRepo.js:12:17
at FSReqWrap.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)
I am using version 16.11.0 of node.
The issue you are facing is with the way you are trying to resolve promise. The Resolve & Reject Functions are available within Promise
let fs = require("fs");
const FILE_NAME = "YOUR PATH TO FILE";
let pieRepo = {
get: () => {
return new Promise((resolve, reject) => {
fs.readFile(FILE_NAME, (err, data) => {
if (err) {
return reject(err);
}
resolve(JSON.parse(data));
});
});
},
};
module.exports = pieRepo;
You can further read about Promise
Resolve and reject are functions available in promises.
Replace your code with this:
let fs = require('fs');
const FILE_NAME = './assets/pies.json';
let pieRepo = {
get: () => {
return new Promise((resolve, reject) => {
fs.readFile(FILE_NAME, (err, data) => {
if (err) {
return reject(err);
}
resolve(JSON.parse(data));
});
});
},
};
module.exports = pieRepo;
Now, you can await the get function.
If this were my project though, the code would look like this:
let fs = require('fs/promises');
const FILE_NAME = './assets/pies.json';
let pieRepo = {
get: async () => {
try {
const data = await fs.readFile(FILE_NAME);
return data;
} catch (err) {
throw new Error(err.message);
}
},
};
module.exports = pieRepo;
If you want to promisify fs.readFile, you can, but promisified versions of fs functions are available through fs/promises.
You have to use Promise, here is a very simple example:
test() {
return new Promise((resolve, reject) => {
if (1 === 1) {
resolve(true)
} else {
reject(false)
}
})
}

can't resolve a promise of a readstream ('csv-parser')

I am trying to parse data from a .csv file, and save it to an array for later use.
I understand the concept of promises, but I have no idea what am I missing in my code that I cannot resolve the Promise and get the value (the string in the .csv file). It while I can view all the data inside the promise (.on('data')) from debugging mode, I just can't save it in order to use it later in my 'try&catch'.
const fs = require("fs");
const csv = require("csv-parser");
const { resolve } = require("path");
async function readCSV(filepath) {
return new Promise(async (resolve, reject) => {
await fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const results = [];
const csvFilePath =
"/languages.csv";
try {
const languages = readCSV(csvFilePath).then((res) => {
return res;
});
console.log(languages);
} catch (e) {
console.log(e);
}
and the output on the console is:
>Promise {<pending>}
No debugger available, can not send 'variables'
** That's from the debugging mode when I pause inside the promise:
https://i.stack.imgur.com/H9nHi.png
You can't try catch a returned promise without the await keyword in an async function.
If you're returning a promise, you need to use the .catch method on the promise.
Also, when you're logging languages you're doing so before the promise resolves because you're not using the await keyword.
I'm sure the promise resolves. Instead, log res inside the .then method.
const fs = require("fs");
const csv = require("csv-parser");
const results = [];
function readCSV(filepath) {
return new Promise((resolve, reject) => {
fs
.createReadStream(filepath)
.pipe(csv())
.on("data", (data) => {
results.push(data);
})
.on("error", (error) => reject(results))
.on("end", () => {
resolve(results);
});
});
}
const csvFilePath = "./languages.csv";
(async () => {
const output = await readCSV(csvFilePath);
console.log(output)
})();

Use of Async await does not wait for the return of function [duplicate]

I would like to use async/await with some filesystem operations. Normally async/await works fine because I use babel-plugin-syntax-async-functions.
But with this code I run into the if case where names is undefined:
import fs from 'fs';
async function myF() {
let names;
try {
names = await fs.readdir('path/to/dir');
} catch (e) {
console.log('e', e);
}
if (names === undefined) {
console.log('undefined');
} else {
console.log('First Name', names[0]);
}
}
myF();
When I rebuild the code into the callback hell version everything is OK and I get the filenames.
Thanks for your hints.
Native support for async/await fs functions since Node 11
Since Node.JS 11.0.0 (stable), and version 10.0.0 (experimental), you have access to file system methods that are already promisify'd and you can use them with try catch exception handling rather than checking if the callback's returned value contains an error.
The API is very clean and elegant! Simply use the .promises member of fs object:
import fs from 'fs';
async function listDir() {
try {
return await fs.promises.readdir('path/to/dir');
} catch (err) {
console.error('Error occurred while reading directory!', err);
}
}
listDir();
Starting with node 8.0.0, you can use this:
const fs = require('fs');
const util = require('util');
const readdir = util.promisify(fs.readdir);
async function myF() {
let names;
try {
names = await readdir('path/to/dir');
} catch (err) {
console.log(err);
}
if (names === undefined) {
console.log('undefined');
} else {
console.log('First Name', names[0]);
}
}
myF();
See https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original
Node.js 8.0.0
Native async / await
Promisify
From this version, you can use native Node.js function from util library.
const fs = require('fs')
const { promisify } = require('util')
const readFileAsync = promisify(fs.readFile)
const writeFileAsync = promisify(fs.writeFile)
const run = async () => {
const res = await readFileAsync('./data.json')
console.log(res)
}
run()
Promise Wrapping
const fs = require('fs')
const readFile = (path, opts = 'utf8') =>
new Promise((resolve, reject) => {
fs.readFile(path, opts, (err, data) => {
if (err) reject(err)
else resolve(data)
})
})
const writeFile = (path, data, opts = 'utf8') =>
new Promise((resolve, reject) => {
fs.writeFile(path, data, opts, (err) => {
if (err) reject(err)
else resolve()
})
})
module.exports = {
readFile,
writeFile
}
...
// in some file, with imported functions above
// in async block
const run = async () => {
const res = await readFile('./data.json')
console.log(res)
}
run()
Advice
Always use try..catch for await blocks, if you don't want to rethrow exception upper.
As of v10.0, you can use fs.Promises
Example using readdir
const { promises: fs } = require("fs");
async function myF() {
let names;
try {
names = await fs.readdir("path/to/dir");
} catch (e) {
console.log("e", e);
}
if (names === undefined) {
console.log("undefined");
} else {
console.log("First Name", names[0]);
}
}
myF();
Example using readFile
const { promises: fs } = require("fs");
async function getContent(filePath, encoding = "utf-8") {
if (!filePath) {
throw new Error("filePath required");
}
return fs.readFile(filePath, { encoding });
}
(async () => {
const content = await getContent("./package.json");
console.log(content);
})();
You might produce the wrong behavior because the File-Api fs.readdir does not return a promise. It only takes a callback. If you want to go with the async-await syntax you could 'promisify' the function like this:
function readdirAsync(path) {
return new Promise(function (resolve, reject) {
fs.readdir(path, function (error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
and call it instead:
names = await readdirAsync('path/to/dir');
This is the TypeScript version to the question. It is usable after Node 11.0:
import { promises as fs } from 'fs';
async function loadMonoCounter() {
const data = await fs.readFile('monolitic.txt', 'binary');
return Buffer.from(data);
}
Node v14.0.0 and above
you can just do:
import { readdir } from "fs/promises";
just like you would import from "fs"
see this PR for more details: https://github.com/nodejs/node/pull/31553
I have this little helping module that exports promisified versions of fs functions
const fs = require("fs");
const {promisify} = require("util")
module.exports = {
readdir: promisify(fs.readdir),
readFile: promisify(fs.readFile),
writeFile: promisify(fs.writeFile)
// etc...
};
Here is what worked for me:
const fsp = require('fs-promise');
(async () => {
try {
const names = await fsp.readdir('path/to/dir');
console.log(names[0]);
} catch (e) {
console.log('error: ', e);
}
})();
This code works in node 7.6 without babel when harmony flag is enabled: node --harmony my-script.js. And starting with node 7.7, you don't even need this flag!
The fsp library included in the beginning is just a promisified wrapper for fs (and fs-ext).
I’m really exited about what you can do in node without babel these days! Native async/await make writing code such a pleasure!
UPDATE 2017-06: fs-promise module was deprecated. Use fs-extra instead with the same API.
Recommend using an npm package such as https://github.com/davetemplin/async-file, as compared to custom functions. For example:
import * as fs from 'async-file';
await fs.rename('/tmp/hello', '/tmp/world');
await fs.appendFile('message.txt', 'data to append');
await fs.access('/etc/passd', fs.constants.R_OK | fs.constants.W_OK);
var stats = await fs.stat('/tmp/hello', '/tmp/world');
Other answers are outdated
You can use the simple and lightweight module https://github.com/nacholibre/nwc-l it supports both async and sync methods.
Note: this module was created by me.

How can I test a catch block using jest nodejs?

How can i test the catch block on a es6 Class
const fs = require('fs');
class Service {
constructor(accessToken) {
this.accessToken = accessToken;
}
async getData() { // eslint-disable-line class-methods-use-this
try {
const data = fs.readFileSync(`${__dirname}/models/mockData.json`, { encoding: 'utf8' });
const returnData = JSON.parse(data);
return returnData;
} catch (err) {
return err;
}
}
}
module.exports = Service;
using jest how can i write the test case to cover the catch block also.
You can mock the method readFileSync from fs to force it to return undefined. JSON.parse(undefined) will throw an error, thus you can check the catch side of the code.
fs.readFileSync = jest.fn()
fs.readFileSync.mockReturnValue(undefined);
First of all, in the catch side you should throw the error. Just returning it is not a good practise when managing errors, from my point of view. But there is people doing it.
const fs = require('fs');
class Service {
constructor(accessToken) {
this.accessToken = accessToken;
}
async getData() { // eslint-disable-line class-methods-use-this
try {
const data = fs.readFileSync(`${__dirname}/models/mockData.json`, { encoding: 'utf8' });
const returnData = JSON.parse(data);
return returnData;
} catch (err) {
throw err;
}
}
}
Having this code, you can actually test your catch block code in two different ways with Jest:
beforeEach(() => {
fs.readFileSync = jest.fn();
});
afterEach(() => {
fs.readFileSync.mockClear();
});
test('Async expect test', () => {
fs.readFileSync.mockReturnValue(undefined);
const result = service.getData();
expect(result).rejects.toThrow();
});
test('Async / await test', async() => {
fs.readFileSync.mockReturnValue(undefined);
try {
await service.getData();
} catch (err) {
expect(err.name).toEqual('TypeError');
expect(err.message).toEqual(`Cannot read property 'charCodeAt' of undefined`);
}
});
Both of them imply to mock the readFileSync method from fs module as I suggested before. You can even mock the whole fs module with Jest. Or you could just mock the JSON.parse. There are plenty of options to be able to test the catch block.
Jest has its own method for testing exception, you can use toThrow. It looks something like this
test('throws on octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow(); // Test the exception here
});
Note
Since your function is asynchronous, try to explicitly define your error, then use await to resolve/reject it, After that you can check for the actual rejection
test('throws on octopus', () => {
await expect(user.getUserName(3)).rejects.toEqual({
error: 'User with 3 not found.',
});
});

reading file with ES6 promises

let arr = [];
function getData(fileName, type) {
return fs.readFile(fileName,'utf8', (err, data) => {
if (err) throw err;
return new Promise(function(resolve, reject) {
for (let i = 0; i < data.length; i++) {
arr.push(data[i]);
}
resolve();
});
});
}
getData('./file.txt', 'sample').then((data) => {
console.log(data);
});
When I use above code and run it in command line using nodejs I get following error.
getData('./file.txt', 'sample').then((data) => {
^
TypeError: Cannot read property 'then' of undefined
How can I solve this?
You'll want to wrap the entire fs.readFile invocation inside a new Promise, and then reject or resolve the promise depending on the callback result:
function getData(fileName, type) {
return new Promise(function(resolve, reject){
fs.readFile(fileName, type, (err, data) => {
err ? reject(err) : resolve(data);
});
});
}
[UPDATE] As of Node.js v10, you can optionally use the built-in Promise implementations of the fs module by using fs.promises.<API>. In the case of our readFile example, we would update our solution to use fs.promises like this:
function getData(fileName, type) {
return fs.promises.readFile(fileName, {encoding: type});
}
Nobody told about util.promisify so I'm going to post, however old the question is.
Why are you having this message?
getData('./file.txt', 'sample').then((data) => {
^
TypeError: Cannot read property 'then' of undefined
getData is a wrapper for fs.readFile file here. fs.readfile is not a thenable (it does not implement a then function). It is built on the other pattern, the callback pattern. The most well-known thenable are Promises, and that's what you want to get from readFile I believe. A little reminder: Mozilla - Promises
So what you can do is either implement it yourself as did #hackerrdave or I would suggest using promisify: this function is a built-in function of Node.js which was implemented to transform the callback-based function into promised based. You will find it here: Node.js Documentation for util.promisfy
It basically does the same as #hackerrdave but it's more robust and built-in node util.
Here's how to use it:
const util = require('util');
const fs = require('fs');
const readFile = util.promisify(fs.readFile)
readFile("path/to/myfile").then(file => console.log(file))
Here is a one-liner as of node 10.2.0:
(async () => console.log(String(await require('fs').promises.readFile('./file.txt'))))();
Yes, it is now out of the box.
As of Node 12+, you can use the fs.promises API.
See an example below:
const { readFile } = require('fs').promises
readFile('./file.txt', { encoding: 'utf8' })
.then((data) => console.log(data))
.catch((error) => console.error(error));
Using async/await
const { readFile } = require('fs').promises
async function readFile(filePath) {
try {
const data = await readFile(filePath, { encoding: 'utf8' })
console.log(data)
} catch (error) {
console.error(error.message)
}
}
readFile('./file.txt')
const getData = (fileName, type) =>
new Promise((resolve, reject) =>
fs.readFile(fileName, type, (err, data) => {
//if has error reject, otherwise resolve
return err ? reject(err) : resolve(data);
})
);
getData('./file.txt', 'utf8')
.then(data => console.log('Data: ', data))
.catch(error => console.log('Error: ', error));
Update for current node As of node 10.0.0 you can now use fs.promises:
const fs = require('fs')
(async function(){
var fileContents = await fs.promises.readFile(FILENAME)
var data = JSON.parse(fileContents)
})()

Categories