Expect throwError not getting coverage - javascript

There is a ValidationError (custom made) being thrown on my invert function if no string is provided:
const invert = (str) => {
if (str === '') throw new ValidationError('String must no be empty.');
return str;
};
This line of code is not getting full coverage by my assertion while using Jest:
expect(() => invert('')).toThrow(ValidationError);
Is there a way to get coverage for this line?

You should have two test cases at least. One test throws an error, and one test is normal. E.g.
index.ts:
export class ValidationError extends Error {
constructor(message) {
super(message);
}
}
export const invert = (str) => {
if (str === '') throw new ValidationError('String must no be empty.');
return str;
};
index.test.ts:
import { invert, ValidationError } from './';
describe('64271662', () => {
it('should throw validation error if string is empty', () => {
expect(() => invert('')).toThrow(ValidationError);
});
it('should return string', () => {
expect(invert('teresa teng')).toBe('teresa teng');
});
});
unit test result with 100% coverage:
PASS src/stackoverflow/64271662/index.test.ts (9.867s)
64271662
✓ should throw validation error if string is empty (4ms)
✓ should return string (1ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 11.042s

Related

How to test code inside setInterval with jest.useFakeTimers()

Here is my simple code myClass.js:
class myClass {
functionOne() {
setInterval(() => {
const age = 10
try {
const userName = "John Smith"
console.log(`Name: ${userName}, age: ${age}`)
} catch (error) {
logger.error('Error', error)
}
}, 5000)
}
}
module.exports = new myClass()
I'm trying to test it with JEST that works fine:
const myClass = require('./myClass')
test("test one", () => {
jest.useFakeTimers();
myClass.functionOne();
expect(setInterval).toHaveBeenCalledTimes(1);
expect(setInterval).toHaveBeenLastCalledWith(expect.any(Function), 5000);
jest.clearAllTimers();
});
Expects work fine and test pass. But sonar-scanner report says that the code inside setInterval() not covered by tests. So how do I really test code inside setInterval?
You can spy on console.log method and assert if it has been called or not. Besides, your example code will never throw an error, which means the catch statement block will never execute.
E.g.
myClass.js:
class myClass {
functionOne() {
setInterval(() => {
const age = 10;
try {
const userName = 'John Smith';
console.log(`Name: ${userName}, age: ${age}`);
} catch (error) {
console.error('Error', error);
}
}, 5000);
}
}
module.exports = new myClass();
myClass.test.js:
const myClass = require('./myClass');
describe('61902581', () => {
test('test one', () => {
jest.useFakeTimers();
const logSpy = jest.spyOn(console, 'log');
myClass.functionOne();
jest.advanceTimersByTime(5000);
expect(logSpy).toBeCalledWith('Name: John Smith, age: 10');
expect(setInterval).toHaveBeenCalledTimes(1);
expect(setInterval).toHaveBeenLastCalledWith(expect.any(Function), 5000);
jest.clearAllTimers();
logSpy.mockRestore();
});
});
unit test results with coverage report:
PASS stackoverflow/61902581/myClass.test.js (10.621s)
61902581
✓ test one (30ms)
console.log
Name: John Smith, age: 10
at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------|---------|----------|---------|---------|-------------------
All files | 90 | 100 | 100 | 90 |
myClass.js | 90 | 100 | 100 | 90 | 9
------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.718s

Representing chained methods and members Typescript through Mocha tests

I am testing a node.js controller file using mocha and chai and i'm unable to mock out the response object in my test
TestController.ts
export class TestController {
static async getTest(req:any, res:any, next:object) {
console.log("Test");
//some code here
res.status(200).json(result.rows);
}
and this works perfectly fine when I call the API, returns the right response etc. But when I try to test this Controller, here is what I have for my test file
Test.ts
it('Get Test method', async function () {
let req = {params: {testid: 12345}};
let res:any = {
status: function() { }
};
res.json = '';
let result = await TestController.getTest(req, res, Object);
});
I am not sure how to represent the response object here. If I just declare the variable res in the following way
let res:any;
I see the following error in my test
TypeError: Cannot read property 'json' of undefined
I am not sure how my response data structure res should be for making this test work.
You should use sinon.stub().returnsThis() to mock the this context, it allows you to call chain methods.
E.g.
controller.ts:
export class TestController {
static async getTest(req: any, res: any, next: object) {
console.log('Test');
const result = { rows: [] };
res.status(200).json(result.rows);
}
}
controller.test.ts:
import { TestController } from './controller';
import sinon from 'sinon';
describe('61645232', () => {
it('should pass', async () => {
const req = { params: { testid: 12345 } };
const res = {
status: sinon.stub().returnsThis(),
json: sinon.stub(),
};
const next = sinon.stub();
await TestController.getTest(req, res, next);
sinon.assert.calledWithExactly(res.status, 200);
sinon.assert.calledWithExactly(res.json, []);
});
});
unit test results with 100% coverage:
61645232
Test
✓ should pass
1 passing (14ms)
---------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
controller.ts | 100 | 100 | 100 | 100 |
---------------|---------|----------|---------|---------|-------------------

Sinon stub function parameter

I have an express app with a router I would like to test with Sinon. I'm not successful in mocking the response parameter that is passed into the request handler and would like some help.
export const pingHandler = (request, response, next) => {
response.status(200).send('Hello world');
}
This is my current test setup using Mocha, Sinon, Chai & sinon-chai. fakeRes.status was never called as expected.
describe("pingHandler", () => {
it("should return 200", async () => {
const fakeResponse = {
status: sinon.fake(() => ({
send: sinon.spy()
}))
};
pingHandler({}, fakeResponse, {});
expect(fakeResponse.status).to.have.been.called;
// => expected fake to have been called at least once, but it was never called
});
});
Here is the unit test solution:
index.ts:
export const pingHandler = (request, response, next) => {
response.status(200).send('Hello world');
}
index.spec.ts:
import { pingHandler } from "./";
import sinon from "sinon";
describe("pingHandler", () => {
it("should return 200", () => {
const mRes = {
status: sinon.stub().returnsThis(),
send: sinon.stub(),
};
pingHandler({}, mRes, {});
sinon.assert.calledWith(mRes.status, 200);
sinon.assert.calledWith(mRes.send, "Hello world");
});
});
Unit test result with 100% coverage:
pingHandler
✓ should return 200
1 passing (8ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.spec.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|

How to test a unhandledRejection / uncaughtException handler with jest

I have handlers for unhandledRejections and uncaughtExceptions:
bin.js
['unhandledRejection', 'uncaughtException'].forEach(event => {
process.on(event, err => logger.error(err));
});
Now I want to test them with jest:
bin.test.js
const bin = require('../bin');
test('catches unhandled rejections', async () => {
const error = new Error('mock error');
await Promise.reject(error);
expect(logger.error).toHaveBeenCalledWith(error);
});
test('catches uncaught exceptions', () => {
const error = new Error('mock error');
throw error;
expect(logger.error).toHaveBeenCalledWith(error);
});
But jest just tells me that there are errors in the tests:
● catches unhandled rejections
mock error
8 | // https://github.com/facebook/jest/issues/5620
9 | test('catches unhandled rejections', async () => {
> 10 | const error = new Error('mock error');
| ^
11 | await Promise.reject(error);
12 | expect(logger.error).toHaveBeenCalledWith(error);
13 | });
at Object.<anonymous>.test (test/bin.test.js:10:17)
● catches uncaught exceptions
mock error
14 |
15 | test('catches uncaught exceptions', () => {
> 16 | const error = new Error('mock error');
| ^
17 | throw error;
18 | expect(logger.error).toHaveBeenCalledWith(error);
19 | });
at Object.<anonymous>.test (test/bin.test.js:16:17)
is there a way to test this?
This might be related: https://github.com/facebook/jest/issues/5620
My test strategy is to install spy onto process.on() and logger.error methods using jest.spyOn(object, methodName). After doing this, these methods have no side effects. Then, you can test your code logic in an isolated environment.
Besides, there are a few things to note:
You should spy the functions before require('./bin') statement. Because when you load the bin.js module, the code will be executed.
You should use jest.resetModules() in the beforeEach hook to resets the module registry - the cache of all required modules. Why? because require() caches its results. So, the first time a module is required, then its initialization code runs. After that, the cache just returns the value of module.exports without running the initialization code again. But we have two test cases, we want the code in module scope to be executed twice.
Now, here is the example:
bin.js:
const logger = require('./logger');
['unhandledRejection', 'uncaughtException'].forEach((event) => {
process.on(event, (err) => logger.error(err));
});
logger.js:
const logger = console;
module.exports = logger;
bin.test.js:
const logger = require('./logger');
describe('52493145', () => {
beforeEach(() => {
jest.resetModules();
});
afterEach(() => {
jest.restoreAllMocks();
});
test('catches unhandled rejections', () => {
const error = new Error('mock error');
jest.spyOn(process, 'on').mockImplementation((event, handler) => {
if (event === 'unhandledRejection') {
handler(error);
}
});
jest.spyOn(logger, 'error').mockReturnValueOnce();
require('./bin');
expect(process.on).toBeCalledWith('unhandledRejection', expect.any(Function));
expect(logger.error).toHaveBeenCalledWith(error);
});
test('catches uncaught exceptions', () => {
const error = new Error('mock error');
jest.spyOn(process, 'on').mockImplementation((event, handler) => {
if (event === 'uncaughtException') {
handler(error);
}
});
jest.spyOn(logger, 'error').mockReturnValueOnce();
require('./bin');
expect(process.on).toBeCalledWith('uncaughtException', expect.any(Function));
expect(logger.error).toHaveBeenCalledWith(error);
});
});
unit test result:
PASS examples/52493145/bin.test.js
52493145
✓ catches unhandled rejections (5 ms)
✓ catches uncaught exceptions (1 ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
bin.js | 100 | 100 | 100 | 100 |
logger.js | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 2.73 s, estimated 4 s
source code: https://github.com/mrdulin/jest-v26-codelab/tree/main/examples/52493145
putting it inside try catch will help:
const error = new Error('mock error');
try {
await Promise.reject(error);
}
catch(error){
expect(logger.error).toHaveBeenCalledWith(error);
}

Chained API's and Jest expects

I currently have an express app that does a bunch of logic on a controller.
One of the steps is to insert a record to the DB ( It uses ObjectionJS models ).
let user = await this.User.query(trx).insert(userData);
In an attempt to mock out the model, I have done :
let mockUser = {
query: jest.fn(() => {
return mockUser;
}),
insert: jest.fn(() => {
return mockUser;
}),
toJSON: jest.fn()
};
With this, I wanted to do an assertion:
expect(mockUser.query().insert).toBeCalledWith({ some: 'data' });
It seems I have missed something. When I run the tests, the code would reach the mock function insert. But jest complaints
You could use mockFn.mockReturnThis() to return this context.
E.g.
index.js:
export async function main(User) {
const trx = 'the trx';
const userData = {};
let user = await User.query(trx).insert(userData);
return user.toJSON();
}
index.test.js:
import { main } from './';
describe('47953161', () => {
it('should pass', async () => {
let mockUser = {
query: jest.fn().mockReturnThis(),
insert: jest.fn().mockReturnThis(),
toJSON: jest.fn().mockResolvedValueOnce({ id: 1 }),
};
const actual = await main(mockUser);
expect(actual).toEqual({ id: 1 });
expect(mockUser.query).toBeCalledWith('the trx');
expect(mockUser.query().insert).toBeCalledWith({});
expect(mockUser.query().insert().toJSON).toBeCalledTimes(1);
});
});
unit test result with coverage report:
PASS src/stackoverflow/47953161/index.test.ts (10.41s)
47953161
✓ should pass (7ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.783s, estimated 13s
source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47953161

Categories

Resources