How can I test a catch block using jest nodejs? - javascript

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.',
});
});

Related

How to resolve promise from a mocked callback function in jest

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

Why is Mocha ignoring asynchronous function calls?

I am writing automated integration tests with Mocha and Chai. Here is a simplified version of the code I am testing:
exports.doSomething = async function (req, res) {
return executeRequest(req.body)
.then((response) => {
console.log("then running");
res.status(200).send(response);
})
.catch((err) => {
console.error(err);
res.status(500).send(err);
}
}
And here is what my test file looks like:
const { doSomething } = require("../../index");
const { assert } = require("chai");
const { stub } = require("sinon");
const req = { body: { *data* } };
const res = {
status: stub().returnsThis(),
send: stub().returnsThis(),
};
it(`Please work`, async () => {
await doSomething(req, res);
}
When that happens, neither the .then block nor the .catch blocks are entered—console.log does not run; res.send and res.status are never called.
Another interesting note: If I remove the async from the test call and save the result of doSomething() to a variable, it shows as a promise. When I include the async, the result of doSomething is undefined.
I am new to Mocha and have no idea why it seems to be ignoring the asynchronicity of the code.
Your doSomething implementation is broken, the promise that the async function returns will fulfill immediately - before executeRequest is done. You should write either
exports.doSomething = function (req, res) {
return executeRequest(req.body)
//^^^^^^
.then((response) => {
console.log("then running");
res.status(200).send(response);
})
.catch((err) => {
console.error(err);
res.status(500).send(err);
})
}
or
exports.doSomething = async function (req, res) {
try {
const response = await executeRequest(req.body);
// ^^^^^
console.log("then running");
res.status(200).send(response);
} catch(err) {
console.error(err);
res.status(500).send(err);
}
}
Only then your test can properly await the doSomething(req, res) call, and mocha won't prematurely kill the process.

Jest returning false positives for Firebase functions?

Having to write some Jest tests for Firebase functions and I'm running into a little trouble. Here's a basic example of what I'm trying to do:
databaseUtils.js
function funcOne() {
return Promise.resolve();
}
function funcTwo() {
return Promise.resolve();
}
index.js
exports.myTest = functions.https.onRequest(async (req, res) => {
try {
await databaseUtils.funcOne();
await databaseUtils.funcTwo();
return res.status(200);
} catch (error) {
console.log(error);
return res.status(403);
}
});
index.test.js
describe('myTest', () => {
it('test 1', async () => {
const req = {}
const res = {
status: jest.fn()
};
await functions.myTest(req, res);
expect(res.status).toHaveBeenCalledWith(200)
});
it('test 2', async () => {
const req = {}
const res = {
status: code => {
expect(code).toBe(403);
}
};
await functions.myTest(req, res);
});
it('test 3', (done) => {
const req = {}
const res = {
status: code => {
expect(code).toBe(403);
done();
}
};
functions.myTest(req, res);
});
})
When I run my tests, Test 1 fails while Test 2 and 3 pass, but with the wrong values:
JestAssertionError: expect(received).toBe(expected) // Object.is equality
Expected: 403
Received: 200
matcherResult: {
actual: 200,
expected: 403,
message: [Function],
name: 'toBe',
pass: false
}
Been running around in circles trying to get this to work properly but I can't figure out, what am I doing wrong?
Once I've got this figured out I'd then mock funcOne / funcTwo to return a rejected promise so I can get the right status code.
onRequest is promise-aware and expected to return a promise of the entire work it does. That there's a dangling promise inside onRequest callback is an antipattern.
It should be:
return auth.decodeToken(req)...
That there are raw promises with then and catch inside async contributes to the problem because async..await is sugar syntax that allows to avoid several common problems with promises. The said problem as well as nested promises could be avoided if this were written as:
try {
const decodedToken = await auth.decodeToken(req);
...
} catch (err) {
...
}

Confusion around 'nested' try/catch statements in Javascript

Essentially I have an async function containing a try/catch that calls another async function also containing a try catch, and I'm getting a bit confused about how to properly implement what I'm doing. Some "pseudocode" showing my current implementation:
const main = async () => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
const secondFunc = async () => {
try {
await performSomeRequestExample();
} catch(err) {
if (err.x === 'x') {
doSomething();
} else {
//********
throw err;
//********
}
}
So what I'm trying to do is get the throw(err) (surrounded by the asterisks) to be caught by the catch in main() which will also call the console.log('Found an error!'), but what currently happens is the error is thrown from secondFunc(), the catch in main() is never hit and I get an unhandled promise rejection.
Any guidance on what I'm doing wrong?
My advice is to minimize using try/catch unless absolutely necessary. With async functions (or any functions that return a Promise object) you can usually simplify things by not worrying about try/catch blocks unless you need to do something specific with certain errors. You can also use .catch rather than try/catch blocks to make things easier to read.
For example your code above could be written like this:
const main = async () => {
const test = await secondFunc().catch(err => {
console.log("Found an error from secondFunc!", err);
throw err; // if you want to send it along to main's caller
});
if (test) {
console.log("Test", test);
}
};
const secondFunc = () => {
return performSomeRequestExample().catch(err => {
if (err.x === "x") {
doSomething();
} else {
throw err;
}
});
};
const performSomeRequestExample = () => Promise.reject("bad");
main().then(
() => console.log("worked"),
err => console.log("failed from main", err)
);
In secondFunc we don't need to use async since we can just return the promise coming back from performSomeRequestExample and handle any failures in the .catch.
You should use
const secondFunc = async () => {
performSomeRequestExample().then(res =>{
console.log(res);
})
.catch(err => {
console.log(err);
}
)
Add a return before the await of performSomeRequestExample.
const secondFunc = async () => {
try {
return await performSomeRequestExample();
} catch (err) {
if (err.x === 'x') {
console.log('x');
} else {
throw err;
}
}
}
or you can also use .catch() after the awaited function.
Another solution can be like this
const main = async() => {
try {
const test = await secondFunc();
console.log(test);
} catch(err) {
console.log('Found an error!');
console.log(err);
}
}
const secondFunc = async () => {
//return await performSomeRequestExample(); //for success
return await performSomeRequestExample(2); //for error
}
const performSomeRequestExample = async(abc=1) => {
return new Promise(function(resolve,reject){
if(abc ==1){
setInterval(resolve("yes"),400);
}else{
setInterval(reject("opps"),400);
}
});
}
main();
Test this code at this link:
https://repl.it/repls/JoyfulSomberTelevision

Mock imported function with jest in an await context

We are creating a node app, based on express, to read from a static local file and return the JSON within the file.
Here is our json-file.js with our route method:
const readFilePromise = require('fs-readfile-promise');
module.exports = {
readJsonFile: async (req, res) => {
try {
const filePath = 'somefile.json';
const file = await readFilePromise(filePath, 'utf8');
res.send(file);
} catch(e) {
res.status(500).json(e);
}
},
};
we use a third party module, fs-readfile-promise which basically turns node readFileSync into a promise.
But we struggle to mock implementation of this third party, to be able to produce two tests: one based on simulated read file (promise resolved) and one based on rejection.
Here is our test file:
const { readJsonFile } = require('../routes/json-file');
const readFilePromise = require('fs-readfile-promise');
jest.mock('fs-readfile-promise');
const resJson = jest.fn();
const resStatus = jest.fn();
const resSend = jest.fn();
const res = {
send: resSend,
status: resStatus,
json: resJson,
};
resJson.mockImplementation(() => res);
resStatus.mockImplementation(() => res);
resSend.mockImplementation(() => res);
describe('json routes', () => {
beforeEach(() => {
resStatus.mockClear();
resJson.mockClear();
resSend.mockClear();
});
describe('when there is an error reading file', () => {
beforeEach(() => {
readFilePromise.mockImplementation(() => Promise.reject('some error'));
});
it('should return given error', () => {
readJsonFile(null, res);
expect(readFilePromise).lastCalledWith('somefile.json', 'utf8'); // PASS
expect(resStatus).lastCalledWith(500); // FAIL : never called
expect(resSend).lastCalledWith({ "any": "value" }); // FAIL : never called
});
});
});
We tried to place readFilePromise.mockImplementation(() => Promise.reject('some error')); at the top, just after the jest.mock() without more success.
The third party code is basically something like:
module.exports = async function fsReadFilePromise(...args) {
return new Promise(....);
}
How can we mock and replace implementation of the module to return either a Promise.resolve() or Promise.reject() depending on our test setup to make our test case pass within res.send() or res.status() method?
The last 2 assertions never pass because the test doesn't wait for the promise in: const file = await readFilePromise(filePath, 'utf8'); to resolve or reject in this case, therefore res.send or res.status never get called.
To fix it, readJsonFile is async, you should await it in the test:
it('should return given error', async () => {
await readJsonFile(null, res);
...
})
How can we mock and replace implementation of the module to return
either a Promise.resolve() or Promise.reject() depending on our test
setup to make our test case pass within res.send() or res.status()
method
Exactly how you're doing it:
readFilePromise.mockImplementation(() => Promise.reject('some error'));
or
readFilePromise.mockImplementation(() => Promise.resolve('SomeFileContent!'));

Categories