Jest Testing - mockReset does not clear the already set values - node.js

I have a lambda which calls an interface. Am trying to mock the functions imported from the interface. In each test case, I have to mock different values. When I run all the test cases, the first mocked value does not get cleared and it is carried over to all Test cases
interface.js
const oracledb=require("oracledb");
module.exports = class dbLayer{
execSql= async (sqlQuery) => {
var result;
var binds={};
result = await oracledb.execute(sqlQuery,binds);
return result
}
}
lambda.js
const qryInterface = require(./interface)
var db;
const handler = async (event, context) => {
var sql= event.query
db = new qryInterface();
var dbResponse = await db.execSql(sql)
return dbResponse
}
test.js
const index = require(./lambda)
const dbMock = require(./interface)
jest.mock(./interface);
var mockResponse1 = {
rows: [
{
col1 : 123
}
]
}
var mockResponse2 = {
rows: [
{
col1 : 456
}
]
}
const firstQryResponse = async () => {
return new Promise((resolve, reject) => {
resolve(mockResponse1);
});
};
const secondQryResponse = async () => {
return new Promise((resolve, reject) => {
resolve(mockResponse2);
});
};
describe("1st test suite", () => {
beforeEach(() => {
dbMock.mockReset();
jest.restoreAllMocks();
});
it("Test 1", asycn () => {
var event={}
event.sql=`SELECT col1 FROM TBL_A WHERE col2 = 'A'`
dbMock.mockImplementation(() => {
return {
execSql:firstQryResponse
}
});
var response = await index.handler(event)
expect(response).toEqual(mockResponse1 )
});
it("Test 2", asycn () => {
var event={}
event.sql=`SELECT col1 FROM TBL_A WHERE col2 = 'B'`
dbMock.mockImplementation(() => {
return {
execSql:secondQryResponse
}
});
var response = await index.handler(event)
expect(response).toEqual(mockResponse2 )
});
});
Test 1 mocks the value set in firstQryResponse and Test 2 mocks secondQryResponse. But what happens is the value mocked for execSql is Not getting reset in Test 2 .i.e. firstQryResponse gets carried over to Test 2.
Because of this, 2nd test fails. If i run them individually, it runs fine. I did try mockReset(), but it does not help as well.
Appreciate any help on this
Thanks
Sadiq

Related

nodejs calling database calls parallel

The module calls three tables(students, subjects, grades)I call three sql queries and somehow managed to call one by one as explained below. the queries are independent of each other. However the select queries are executed one by one, first student, then subjects, then grades using await.
studentmodel.calls are only for executing select quesries from the database and is in one module. Other functions are defined in a separate module
The logic can execute the three selct queries(database calls) in parallel, then aggregate and process all the data together. Please let me know how to modify so that the database calls can execute independent, then move to process all data together
processing module -main start call
const mainstart = async () => {
let students = 0;
const getStudentData = await getAllStudents();
/** checking a condition if getEmployeeData responce is not empty */
if (getStudentData.length > 0) {
const studentData = await processData(getStudentData);
return 1;
} else {
return 0;
}
};
same file secondcall to the function getAllStudents
const getAllStudents = async () => {
try {
return await studentmodel.getAllStudents();//database call 1
} catch (err) {
// console.log("processing", err)
}
};
const processData = async (getStudentData) => {
try {
let studentData = [];
const subjectsData = await studentModel.getStudentSubjects();//database call 2
const gradesData = await studentModel.getStudentGrades();//database call 3
await Promise.all(getStudentData.map(async (singleObject) => {
let subjects = await processSubjects(subjectsData, singleObject.student_log);
let grades = await processGrades(gradesData, singleObject.student_log);
//Some processing on sigleobject, subjects and grades to populate studentData array
}));
return studentData;
} catch (err) {
console.log("processing", err)
}
};
const processSubjects = async (result, Name) => {
let subjectsArr = [];
const processArray = result.filter(ele => ele.student_log == Name)
processArray.map((singleObject) => {
subjectsArr.push({
name: singleObject.name,
startDate: singleObject.startDate,
});
})
return subjectsArr;
}
const processGrades = async (result, Name) => {
let gradesArr = [];
const processArray = result.filter(ele => ele.student_log == Name)
processArray.map((singleObject) => {
gradesArr.push({
id: singleObject.id,
name: singleObject.name,
});
})
return gradesArr;
database calls module/studentModel
const getAllStudents = async () => {
try {
/** Populating all students */
const sqlQuery = `SELECT * FROM STUDENTS`;
let [result] = await bigQuery.query({
query: sqlQuery,
location: 'US'
});
return result;
} catch (err) {
return false;
}
};
const getStudentSubjects = async () => {
try {
/** Populating all skills */
const sqlQuery = `SELECT * FROM Subjects`;
let [result] = await bigQuery.query({
query: sqlQuery,
location: 'US'
});
return result;
} catch (err) {
return false;
}
};
const getStudentGrades = async () => {
try {
/** Populating all students */
const sqlQuery = `SELECT * FROM GRADES`;
let [result] = await bigQuery.query({
query: sqlQuery,
location: 'US'
});
return result;
} catch (err) {
return false;
}
};
While I didn't probably fully understand what your question is, I had a go with your code.
I simulated your studentmodel functions with setTimeout and made the code like so that it first fetches all students. After fetching all students, it fetches the subjects and the grades "in parallel" by utilising Promise.all. After we have fetched our students, subjects and grades, we pass all of those to processData function where you can process all of the data however you want.
In case you would also like to fetch the students "in parallel" with the subjects and grades, just change the Promise.all part like so:
const [studentData, studentSubjects, studentGrades] = await Promise.all(
[
getAllStudents(),
getStudentSubjects(),
getStudentGrades()
]);
And remove the const studentData = await getAllStudents(); line and the if-clause. Because you had the if(studentData.length > 0) in your code, I assumed that we only want to fetch subjects and grades if there are students and therefore that needs to be done first, separately.
Note that if you want to do all three in parallel, you cannot use studentData when calling getStudentSubjects or getStudentGrades.
// STUDENTMODEL
const getAllStudents = async () => {
// Simulate SQL query
console.log("Fetching students");
return new Promise(resolve =>
setTimeout(() => {
resolve(["Student 1", "Student 2"])
}, 1000));
};
const getStudentSubjects = async () => {
// Simulate SQL query
console.log("Fetching subjects");
return new Promise(resolve =>
setTimeout(() => {
resolve(["Subject 1", "Subject 2"])
}, 1500));
};
const getStudentGrades = async () => {
// Simulate SQL query
console.log("Fetching grades");
return new Promise(resolve =>
setTimeout(() => {
resolve(["Grade 1", "Grade 2"])
}, 1500));
};
const mainstart = async () => {
// Fetch student data from database
const studentData = await getAllStudents();
if (studentData.length > 0) {
// Use Promise.all to wait until both student subjects and
// student grades have been fetched
// The results are destructured into
// studentSubjects and studentGrades variables
const [studentSubjects, studentGrades] = await Promise.all(
[
getStudentSubjects(studentData),
getStudentGrades(studentData)
]);
// Everything is fetched, process it all
processData([studentData, studentSubjects, studentGrades]);
return 1;
} else {
return 0;
}
};
const processData = (allData) => {
console.log("Processing all data");
console.log(allData);
// Process data somehow
};
(async () => {
console.log('start');
await mainstart();
console.log('end');
})();

mock a registered resolver in awilix

I have used awilix to be able to have dependency injection in javascript to be able to have easier test. but now I want to mock a resolver that is set in my container for only a set of tests
In other words, I have a resolver that I want to mock it in my test for some reasons, (it is costly to call it so many times and it is a time consuming network call.) thus, I need to mock it in many of my tests for example in a test which is called called b.test.js, but I want it to call the actual function in a.test.js
here is my awilix config
var awilix = require('awilix');
var container = awilix.createContainer({
injectionMode: awilix.InjectionMode.PROXY,
});
var network = () => {
return new Promise((resolve, reject) => {
setTimeout(() => { resolve('data') }, 3000);
});
}
module.exports = container.register({ network: awilix.asValue(network) });
my test is
const container = require('../container');
container.register({
heavyTask: awilix.asValue(mockFunction),
});
describe('b', () => {
it('b', async () => {
const result = await container.resolve('network')();
});
});
somehow you've already done it
but don't config container like what you've done, because this way you're gonna have a single object of container, so if you change that object it'll be changed in all tests, instead do it this way
const awilix = require('awilix');
const heavyTask = () => new Promise((resolve, reject) => {
setTimeout(() => {
resolve('actual run');
}, 3000);
});
const configContainer = () => {
const container = awilix.createContainer({
injectionMode: awilix.InjectionMode.PROXY,
});
return container.register({
heavyTask: awilix.asValue(heavyTask),
});
}
module.exports = configContainer;
it seems you already know that you can overwrite your registrations, which might be the only vague part
so a.test.js can be written as
const { describe, it } = require('mocha');
const { expect } = require('chai');
const configContainer = require('../container');
const container = configContainer();
describe('a', () => {
it('a', async () => {
const res = await container.resolve('heavyTask')();
expect(res).to.eq('actual run');
});
});
and test b can be written as something like this
const awilix = require('awilix');
const { describe, it } = require('mocha');
const { expect } = require('chai');
const configContainer = require('../container');
const container = configContainer();
const heavyTask = () => 'mock run';
container.register({
heavyTask: awilix.asValue(heavyTask),
});
describe('b', () => {
it('b', async () => {
const res = await container.resolve('heavyTask')();
expect(res).to.eq('mock run');
});
});

else path not taken on a try catch block

disclaimer: this is part of a course I am taking and a practice task. I am having quite hard time wrapping my head around the test chapter of a course I am taking. Given the following class, I have to write test to it but I see the error else path not taken on my return results; line. Which else is it talking about?
import DB from './DB';
import ErrorLogger from './ErrorLogger'; // ===> if path not taken here
class ChapterSevenJest {
constructor() {
if (!ChapterSevenJest.instance) {
ChapterSevenJest.instance = this;
}
return ChapterSevenJest.instance;
}
_db = new DB();
getData = async (gradeId, teamId) => {
let results = [];
try {
results = await this._db.getData(gradeId, teamId);
} catch (error) {
ErrorLogger.register(
error
);
}
return results; // else path not taken here
};
}
const JestPractice = new ChapterSevenJest();
export default JestPractice;
the test:
import DB from './DB';
import ErrorLogger from './ErrorLogger';
import JestPractice from './JestPractice';
describe('service', () => {
const gradeId = 11;
const teamId = 1;
let spyLogs;
beforeEach(() => {
spyLogs = jest.spyOn(ErrorLogger, 'register');
spyLogs.mockReturnValue(true);
});
afterEach(() => {
spyLogs.mockReset();
});
it('should return data ot a grade and team', async () => {
const spyDB = jest.spyOn(
DB.prototype,
'getData'
);
const stat = [
{
"score" : 100,
"rank": 2
}
]
spyDB.mockResolvedValue(stat);
const results = await JestPractice.getData(
gradeId,
teamId
);
expect(spyDB).toHaveBeenCalledWith(gradeId, teamId);
expect(results).toHaveLength(1);
expect(results[0].score).toStrictEqual(100);
expect(results[0].rank).toStrictEqual(2);
});
it('should return empty on error', async () => {
const spyDB = jest.spyOn(
DB.prototype,
'getData'
);
spyDB.mockRejectedValue('error');
const results = await JestPractice.getData(
gradeId,
teamId
);
expect(spyDB).toHaveBeenCalledWith(gradeId, teamId);
expect(results).toHaveLength(0);
expect(spyLogs).toHaveBeenCalledWith(
"error"
);
});
});
There is only 1 if statement which is
if (!ChapterSevenJest.instance) {
ChapterSevenJest.instance = this;
}
You need to have a test where ChapterSevenJest.instance is truthy so that the if block doesn't execute.

How to test while loop code using jest and nodejs

I'm having a hard time doing some test on while loop using jest. This is the code I want to test but don't know how to do it.
const SHA256 = require('crypto-js/sha256')
class Block {
constructor(index, timestamp, data, prevHash = "") {
this.index = index
this.timestamp = timestamp
this.data = data
this.prevHash = prevHash
this.hash = this.calculateHash()
this.nonce = 0
}
calculateHash() {
return SHA256(this.index + this.prevHash + this.timestamp + JSON.stringify(this.data) + this.nonce).toString()
}
mineBlock(difficulty) {
while(this.hash.substring(0, difficulty) !== Array(difficulty + 1).join('0')) {
this.nonce++
this.hash = this.calculateHash()
}
}
}
module.exports = Block
This is what I've done so far
const Block = require('../block')
const BlockClass = new Block()
describe('Block Class', () => {
it('constructor', () => {
const obj = new Block(1, 2, 3, 4, 0)
expect(obj.index).toBe(1)
expect(obj.timestamp).toBe(2)
expect(obj.data).toBe(3)
expect(obj.prevHash).toBe(4)
expect(obj.nonce).toBe(0)
})
})
describe('hash', () => {
it('should be string', () => {
expect(typeof BlockClass.calculateHash()).toBe('string')
})
})
I'm pretty new with jest and unit testing and I find it really nice skills to have.
You can do something like this.
const Block = require('../block')
describe('mineBlock', () => {
let block = new Block()
it('should do <something>', () => {
block.mineBlock(5)
expect(block.nonce).toBe('<something>')
expect(block.hash).toBe('<something>')
})
})
Please replace <something> with real values you want.

Async await not working in forEach

I have the following code:
async function goodUsers(users) {
const filteredUsers = [];
users.forEach(async (userInstance) => {
console.log('TEST1');
const fromUserContacts = await db.userContactInvite.findAll({
where: {
fromUserId: userInstance.id,
},
});
console.log('TEST2');
await fromUserContacts.forEach((fromUserContact) => {
console.log('TEST3');
const userJson = fromUserContact.toJSON();
userJson.contactState = 'INVITATION_SENT';
filteredUsers.push(userJson);
});
console.log('TEST4');
});
console.log('FILTERED', filteredUsers);
return filteredUsers;
}
When I call goodUsers I get the following output:
TEST1
FILTERED
TEST2
TEST3
TEST4
FILTERED should be last (obviously).
I tried various options but I seem to be not understanding something here. Do you guys notice what's going on?
This's a correct behavior, async/await only affect the function where they are used. So you need to replace forEach which calls callback for each element, to for operator:
async function goodUsers(users) {
const filteredUsers = [];
for(user in users) {
console.log('TEST1');
const fromUserContacts = await new Promise(resolve => setTimeout(() => resolve(['c1', 'c2']), 500));
console.log('TEST2');
fromUserContacts.forEach(fromUserContact => {
console.log('TEST3');
filteredUsers.push('json');
});
console.log('TEST4');
}
console.log('FILTERED', filteredUsers);
return filteredUsers;
}
goodUsers(['u1', 'u2']);

Resources