Why does fs.writeFileSync() throws "is not a function" error? - javascript

I am trying to save some data in a file using fs.writeFileSync, but it doesn't work and I cannot figure out why. It throws the following error: Unhandled Rejection (TypeError): fs.writeFileSync is not a function
In my app I use it like this :
const fs = require('fs');
const stateFile = "./usersStateFile";
const saveLastEventSequenceId = (sequenceId) => {
try {
fs.writeFileSync(stateFile, sequenceId);
} catch (err) {
throw err;
}
};
The sequenceId is a number and the ./usersStateFile doesn't exist, but fs.writeFileSync() should create it if it doesn't exist.
What might be the problem ?

Import fs module like so:
const fs = require('fs'); // or `import * as fs from 'fs'` if you're using ES2015+
const stateFile = "./usersStateFile";
const saveLastEventSequenceId = (sequenceId) => {
try {
fs.writeFileSync(stateFile, sequenceId);
} catch (err) {
throw err;
}
};
You were calling fs.writeFileSync() without having a variable named fs that's defined in your scope. In this case, fs evaluates to undefined, which caused the error when trying to invoke its in-existent function member.

Related

Sinon Stub is not throwing an error when fs.readFileSynch also throws an error

After going through all related issues regarding stubbing readFile/Sync with Sinon /Chai/ Mocha, the test is failing.
There's a basic getFile function that retrieves a file:
function getFile(path) {
const file = fs.readFileSync(path, "utf8)
return file;
}
module.exports = {getFile}
and I want to create a test where getFile should throw an error if fs.readFileSync also throws an error:
it('should throw an error if fs.readFileSync throws an error', () => {
I tried:
it('should throw an error if fs.readFileSync throws an error', () => {
const error = new Error('some error message')
const myStub = sinon.stub(fs, "readFileSync")
myStub.throws(error)
const filePath = "/Project/test.js"
const gFile = index.getFile(filePath)
try {
if(myStub.error === true) {
gFile(error)
} catch (error) {
expect(myStub).to.throw(error)
What I got was:
1 failing
Error: some error message
at Context.
at process.processImmediate
See the chai expect docs on throw(). There is this example:
var badFn = function () { throw new TypeError('Illegal salmon!'); };
expect(badFn).to.throw();
You see that expect(badFn) gets badFn not badFn(), so nowhere in the test is badFn actually called.
This means that expect calls badFn. And actually, expect needs to be the one to call it because it needs to catch the error.
So in your code, try this:
const stub = sinon.stub(fs, 'readFileSync').throws();
const callGetFile = () => {
index.getFile('some_file');
};
expect(callGetFile).to.throw();
Try putting your error function in the Sinon throw method as below.
myStub.throws(new Error('some error message'));

How to unit test an Express Controller with Jest

I have been getting terribly confused with how to test my controller functions. I realize that I have to mock my dependencies, request, response, and the controller functions. Here's what I have so far:
OrdersController.js
const OrderService = require('../services/orderServices')
module.exports = class OrdersController {
static async apiGetOrders(req, res, next) {
try {
const orders = await OrderService.getOrders()
return res.status(200).json(orders)
} catch (error) {
return res.status(500).json({ error: 'Unable to get orders' }) // 500, Internal Service Error, generic
}
}
static async apiPostOrder(req, res, next) {
// All good, create an orderDocument
try {
const orderDocument = {
_id: null, // undefined at this point Mongo creates this _id for us
orderId: req.body.orderId,
cookies: req.body.cookies,
daySelected: req.body.daySelected,
timeSelected: req.body.timeSelected,
userInfo: req.body.userInfo,
createdAt: new Date(),
}
await OrderService.addOrder(orderDocument)
return res.status(201).send('success') // status OK, something was Created
} catch (error) {
return res.status(500).json({ error }) // 500, Internal Server Error
}
}
OrdersController.spec.js
import OrderService from '../services/orderServices'
import { mockOrder, mockOrders } from '../mocks/fixtures'
import OrdersController from '../controllers/ordersController'
jest.mock('../controllers/ordersController.js')
const mockRequest = () => {
return {}
}
const mockResponse = (mockOrders) => {
const res = {};
res.status = jest.fn().mockReturnValue(200);
res.json = jest.fn().mockReturnValue(mockOrders);
return res;
}
// #3 Test the OrdersControllers
// mock dependencies: req, res, and spyOn the controller functions
describe('Orders Controller', () => {
test('[Positive], should call OrderService.getOrders and receive status 200', async () => {
jest.spyOn(OrdersController, 'apiGetOrders')
const req = mockRequest()
const res = mockResponse(mockOrders)
await OrdersController.apiGetOrders(req, res)
expect(res.status).toHaveBeenCalledWith(200)
expect(res.json()).toEqual(mockOrders)
})
test('[Negative], error yields status 500', async () => {
jest.spyOn(OrdersController, 'apiGetOrders')
const req = mockRequest()
const res = mockResponse({status: 500, error: 'Unable to get orders'})
await OrdersController.apiGetOrders(req, res)
expect(res.status).toHaveBeenCalledWith(500)
expect(res.json()).toEqual(error)
})
})
I'm trying to test the happy path and the negative path on the get request. I followed this expample, https://codewithhugo.com/express-request-response-mocking/, and read all of the jest docs, https://jestjs.io/docs/mock-functions. The error that I receive is:
Questions:
Am I actually writing the tests correctly?
Am I also supposed to mock the OrderService?
Should I use Sinon or is Jest more than sufficient?
I am also new to Jest and am struggling with finding documentation that goes to enough detail to suggest to me what I'm doing wrong. But in your case, I think you might be spying on the wrong thing. The OrdersController is the subject of the test, so I don't believe that you should be mocking that. Rather you should spy on OrdersController's dependency, Orderservice and mock its methods.
Also, its not clear to me why you have next in
static async apiGetOrders(req, res, next)
You don't seem to use it in the body of the method anywhere, so hanging any testing off that value, probably won't work either.

proper import/export and binding for ESM/Node

I have some pre-ES6 code that was working fine with require-type syntax, and am trying to port this to ESM friendly methodology. I have much of the js to mjs coding ported, and am working on a dynamic file loading block, that is giving me fits. I've gotten through enough googling and debugging to realize my issue is now with the syntax of the export function in the "events" file, which I'm using default syntax to support.
First the calling file:
async function eventLoad(eventDir = './events/')
{
await fs.readdir(`./events/`,function (err, files){
if (err)
{
console.error(`${error}: Error loading event: ${err}`);
return;
}
else
{
const events = files.filter(file=>file.endsWith('.mjs'));
for( const file of events)
{
console.log(`event file name is ${file}`);
const {default: event} = import(join(`./events/`,`${file}`));
const eventName = file.split('.')[0];
dBot.on(eventName, event.bind(null,dBot));
console.log(`${success} Loaded event ${eventName}`);
}
}
})
}
eventLoad();
// please ignore the missing promise on the import...I'll be adding it shortly. :)
I get an error:
TypeError: Cannot read property 'bind' of undefined
my export in the other file declares like this:
export default async (client, message) =>
{
...
knowing from googled issues that much of the likelihood rests in the improper definition in the file that is exporting the fuction, I tried playing with the syntax:
async function ProcessMessages (client, message)
{
...
}
export {ProcessMessages as default };
but alas, no help. I'm sure the issue is properly handling the export syntax, but I'm learning this as I go, and would appreciate any help you could provide, thanks!!!
Update: (based on solution, here is my code changes in case anyone else wants to leverage)
async function eventLoad(eventDir = './events/')
{
let files;
try {
files = await promiseBasedReaddir(`${eventDir}`);
}
catch (err) {
console.error(`${error}: Error loading event: ${err}`);
return;
}
const events = files.filter(file=>file.endsWith('.mjs'));
for( const file of events)
{
console.log(`event file name is ${file}`);
import(`${eventDir}${file}`)
.then(function( {default: event} ){
const eventName = file.split('.')[0];
dBot.on(eventName, event.bind(null,dBot));
console.log(`${success} Loaded event ${eventName}`);
})
.catch(function(err){
console.error(`${error}: Error loading event: ${err}`);
return;
})
}
}
eventLoad();
On this line, you do a dynamic import:
const {default: event} = import(join(`./events/`,`${file}`));
import will always return a Promise. The Promise object doesn't have a .bind method, only functions do. You need to await or .then() import()'s return value. So you can't "ignore the missing promise on the import".
A second issue:
await fs.readdir(`./events/`, ...´
fs.readdir doesn't return a promise, so await-ing it is useless. It is functionally the same as await undefined;. The outer function will return before the callback has finished.
If you use a readdir function that returns a promise, you can simplify things a lot.
import { promises as fsPromises } from 'fs';
const { readdir: promiseBasedReaddir } = fsPromises;
// OR alternatively:
import { readdir } from 'fs';
import { promisify} from 'util';
const promiseBasedReaddir = promisify(readdir);
async function eventLoad(eventDir = './events/')
{
let files;
try {
files = await promiseBasedReaddir(`./events/`);
} catch (err) {
console.error(`${error}: Error loading event: ${err}`);
return;
}
const events = files.filter(file=>file.endsWith('.mjs'));
for( const file of events)
{
console.log(`event file name is ${file}`);
const {default: event} = await import(join(`./events/`,`${file}`));
const eventName = file.split('.')[0];
dBot.on(eventName, event.bind(null,dBot));
console.log(`${success} Loaded event ${eventName}`);
}
}
Promisify will transform a callback-using function to a promise-returning function. The fs module has promise-based functions, so you don't need to use Promisify if you just import those.

Firestore realtime listener error unhandled in node.js

I have made a function that I am exporting using node.js called return_match_uid. I am importing the function in another express routing file and am using async await, with try and catch to handle the error. But somehow, the errors produced by return_match_uid always slip and are unhandled, even though I am using the error handling for the realtime listener recommended by Firestore doc
Here is the function:
exports.return_match_uid = function return_match_uid() {
return new Promise((resolve, reject) => {
const unsub = db.collection('cities').onSnapshot(() => {
throw ("matching algo error");
resolve();
unsub();
}, err => {
console.log(err);
})
})
})
In another express router file, I am calling the function:
const Router = require('express').Router;
const router = new Router();
const {return_match_uid} = require("./match_algo");
router.get('/match', async (req, res) => {
try {
var match_user = await return_match_uid(req.query.data, req.query.rejected);
res.send(match_user);
}
catch (error) {
console.log("Matching algorithm return error: " + error);
}
})
The error I am throwing inside the function: matching algo error do not get caught by either the err => {console.log(err);}) in the function nor the try catch block in the router. It slips and causes my app to crash. It shows the following error:
throw "matching algo error!";
^
matching algo error!
(Use `node --trace-uncaught ...` to show where the exception was thrown)
[nodemon] app crashed - waiting for file changes before starting...
I am throwing an error inside matching algo error because I have some other codes in there, and there is a possibility that it produces an error. If it does, I would like to make sure that it gets handled properly.

Testing async/await method. Exception not being caught by Jest in unit test

I'm attempting to test some code that uses await and async using Jest. The problem I'm having is an exception is thrown, but Jest doesn't seem to catch it.
For example, here is run method that checks to see if session.url is present. If not, an exception is thrown:
const args = require('args');
const fs = require('fs');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);
// Loads JSON configuration file
module.exports.loadConfigFile = async (filename) => {
const contents = await readFile(filename, 'utf8');
return JSON.parse(contents);
};
// Prepare session ensuring command line flags override config
module.exports.prepareSession = async (flags) => {
if(!flags.config) return flags;
const config = await this.loadConfigFile(flags.config);
return {...config, ...flags};
};
// Runs the race application
module.exports.run = async (flags = {}) => {
const session = await this.prepareSession(flags);
if(!session.url) throw new Error('A valid URL is required');
};
Here I test to see if an exception is thrown:
describe('Race Module', () => {
test('Throws exception if no URL is provided', async () => {
const runner = await race.run();
expect(runner).toThrowError();
});
...
The test runs and it appears an exception is thrown but jest hasn't caught it and it doesn't pass:
Race Module
✕ Throws exception if no URL is provided (3ms)
● Race Module › Throws exception if no URL is provided
A valid URL is required
at Object.<anonymous>.module.exports.run (src/race.js:23:27)
at <anonymous>
Any ideas where I'm going wrong?
My initial thought was to chain catch(() => {}) to race.run() in the test but I am not entirely sure how I would test that. That might not even be the problem.
The fix was to use rejects.toThrow. But, note that this functionality is currently broken in master. I had to use a beta branch. See here: https://github.com/facebook/jest/pull/4884
test('Throws exception if no URL is provided', async () => {
await expect(race.run())
.rejects
.toThrow('A valid URL is required');
});

Categories