Sinon stub not allowing for test coverage on actual function - node.js

Here is the function under test:
export const callStoredProcedure = async (procedureName, procedureArgs)
=> {
let resultSet: any;
try {
await knexClient.transaction(async trx => {
await trx.raw(`CALL ${procedureName}(${procedureArgs});`).then(result => {
if (result[0] && result[0][0]) {
resultSet = JSON.stringify(result[0][0]);
}
});
});
} catch (err) {
console.log(err);
}
return resultSet;
};
And here I'm mocking the knexClient in a beforeEach block:
let knexClientMock;
beforeEach(() => {
// Stub Data source clients
const rawStub = sinon.stub().resolves([{ affectiveRows: 0, insertId: 0 }]);
knexClientMock = sinon.stub(knexClient, 'transaction').callsFake(
async (): Promise<any> => {
return {
raw: rawStub,
};
},
);
});
However, when I run my tests the if statement if (result[0] && result[0][0]) never runs and Jest coverage shows everything is uncovered from the raw statement down to the catch.
The mocking seems to be working correctly in that it uses the mock knexClient.transaction, but it leaves the rest of the lines uncovered. I think I just want to mock the raw function with a mock promise result and allow the .then function to run.
How can I mock the knexClient.transaction.raw function only and allow the rest of the code to have test coverage?

The issue is that your test stubs do not correctly mock the objects being used:
The "transaction" method in your code takes a single argument (a callback function) and invokes this callback, passing it a transaction executor that has an asynchronous "raw" method.
Your test stubs make the "transaction" method return an object that has an async raw method.
When writing unit tests for your code, it is very important to understand how your code works, first. Otherwise it is extremely easy to write meaningless tests that won't protect against introducing bugs.

Related

How to mock a function from the same module as the function being tested

So, I have two methods on a Node project:
export function methodA() {
const response = await methodB();
return response.length ? 'something' : 'else';
}
export function methodB() {
const array = await getData(); // Access database and make API calls
return array[];
}
methodA calls methodB and methodB makes stuff that I don't care right now for my unit testing purposes, so I want to mock methodB so it will return an empty array and won't try to make any database or API calls. The issue is that I can't actually mock methodB, as my test is still calling the actual function.
Here's my test:
describe('method testing', () => {
it('calls method', async () => {
const response = await myModule.methodA();
expect(response).toBe('else');
});
});
That test is failing, because jest is still calling the actual methodB which is meant to fail, as it can't connect to the database or reach APIs, so, this is what I tried doing to mock methodB:
Spying on the method:
import * as myModule from '#/domains/methods';
jest.spyOn(myModule, 'methodB').mockImplementation(() => [] as any);
// describe('method testing', () => {...
Mocking the entire file except for the methodA:
jest.mock('#/domains/methods', () => {
const originalModule = jest.requireActual('#/domains/methods')
return {
...originalModule,
methodB: jest.fn().mockReturnValue([])
}
});
// describe('method testing', () => {...
I have also tried:
Mocking methodB inside each test and inside describe
Spying methodB inside each test and inside describe
Some variations of those examples I wrote above
I'm not entirely sure on what to do right now, so any light would be appreciated.
#Update: Altough the problem is similar, this is not a duplicate question and this (How to mock functions in the same module using Jest?) does not answer my question.

Function is a mock in Jest, but not really mocking

I mocking a function, and for some reason, the code is still running the original function,
I made sure that the function is indeed mocked using isMockFunction,
import { getTables, getExportFiles } from 'rds-formatter';
jest.mock('rds-formatter', () => {
return {
...jest.requireActual('rds-formatter'),
getExportFiles: jest.fn(() => Promise.resolve('return something')),
});
Now the thing is Im testing an asyncGenerator function
export async function *getTables(assetId: string, payload: Record<string, unknown>) {
const { parquetFiles, tablesInfoFile } = await getExportFiles(s3Client, payload);
return parquetFiles }
My test looks like this:
it('', async () => {
console.log(jest.isMockFunction(getExportFiles)); //returns true
const data = getTables('gfsdg', payload)
expect((await data.next()).value).toBe('')
});
The error I'm getting is s3Client.listObjectsV2 is not a function This error is from inside the function I'm trying to mock, I know this happens because I'm not passing a real client, but from my understanding the compiler shouldn't even go inside the mocked function, no?

How to make Mocha unit test wait for async variable

I am very new to unit testing and I am trying to write a unit test on a function that reads from my database, but because the read is asynchronous the value is always undefined in my Mocha unit test. How can I do an assert on a variable which is declared asynchronously?
Unit Test:
const homeController = require('../controllers/homeController');
describe('Testing home_get function', function () {
it ('should render the home page', function () {
const req = {};
homeController.home_get(req, null, () => {});
console.log("numberUsers", numberUsers);
assert.ok(numberUsers > 0);
});
});
Function I am trying to test:
module.exports.home_get = async (req, res) => {
requestTime = Date.now();
numberUsers = await User.countDocuments({});
numberViewers = await Viewer.countDocuments({});
numberAccreditors = await Accreditor.countDocuments({});
res.status('200').render('home', {numberUsers: numberUsers, numberViewers: numberViewers, numberAccreditors: numberAccreditors});
};
When I do an assert statement on the requestTime variable it works as intended but when I do it on the async variables the value is always undefined and I am not sure how to get the unit test to work on these async variables, also.... how would you then do a unit test to check the res.status is correct when you render home?
the main goal in unit testing is to only test 1 layer of logic, such as the function itself you have presented. every other thing must be a mock, otherwise it is an integration test that you require and not a unit test.
in this case, you are trying to actually run a query on a database, but it is not actually up and working, so what you get is undefined.
i suggest the following:
if you want to actually query the database, run an integration test instead where it will actually work. integration tests are loading the whole application to test the interaction between all of the components.
to complete the unit test itself, you should mock the database such as it returns what you expect it to return, so only those 5 lines in the function is actually running.
You have two way to do that.
You can await your function in the test:
it ('should render the home page', async function () {
const req = {};
await homeController.home_get(req, null, () => {});
console.log("numberUsers", numberUsers);
assert.ok(numberUsers > 0);
});
or use the test callback function:
it ('should render the home page', function (done) {
const req = {};
homeController.home_get(req, null, () => {}).then(() => {
console.log("numberUsers", numberUsers);
assert.ok(numberUsers > 0);
done();
});
});
But in either case your function should return a value on which you can make the assert. There, the numberUsers is never declared and will still be undefined.
edit: Except if registered in the globals object because not using let, const or var. This is something you probably do not want to do.

Using jest spyOn cannot detect methods being called inside try-catch block

The problem I'm having is that Jest is reporting setResultsSpy is being called 0 times when in fact, I know that it is being called. I know this by putting console.log(results) under the const results = await getFileList(data.path); in my code and was able to see results returned.
My guess right now is that try-catch blocks creates a local scope, which is causing those calls to not be registered. If this is true, my question is "how can I test if those methods have been called"?
// test_myFunction.js
test((`myFunction with valid path should return list of files`), () => {
const actions = {
setMsg: () => { },
setButton: () => {},
setResults: () => {},
setAppState: () => {}
};
const setMsgSpy = jest.spyOn(actions, 'setMsg');
const setSubmitButtonStateSpy = jest.spyOn(actions, 'setButton');
const setResultsSpy = jest.spyOn(actions, 'setResults');
const setAppStateSpy = jest.spyOn(actions, 'setAppState');
const returnedFileList = [
'file1.pdf',
'file2.pdf',
'file3.pdf',
];
const requestConfig = {
component: COMPONENTS.myComponent,
request: RequestTypes.REQUEST,
data: {path: 'folder1'},
actions
};
processRequest(requestConfig)
expect(setMsgSpy).toHaveBeenCalledTimes(1);
expect(setMsgSpy)
.toHaveBeenCalledWith('loading');
expect(setButtonSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledWith(returnedFileList);
expect(setAppStateSpy).toHaveBeenCalledTimes(1);
expect(setAppStateSpy).toHaveBeenCalledWith('confirm');
});
_
// myFunction.js
async function processRequest({
component,
request,
data,
actions,
}){
if (component === COMPONENTS.myComponent) {
const path = data.path.trim();
switch (request) {
case RequestTypes.REQUEST:
actions.setMsg('message');
actions.setButton('disabled');
try {
const results = await getFileList(data.path);
actions.setResults(results);
actions.setAppState('confirm');
} catch (e) {
actions.setError(e);
actions.setAppState('error');
}
}
break;
default:
break;
}
}
The the problem was Jest was failing out of the test before the results from getFileList() execution has completed since getFileList() is an async function.
The solution is for the test to handle the execution asynchronously as per the documentation. There are 4 ways to solve this problem:
Use callbacks
Use .then() and .catch() on the returned promise (see docs on .then() here and .catch() here)
Use .resolves() or .rejects() Jest methods on expect() to let Jest resolve the promise.
Use Async-Await syntax by declaring the test anonymous function as async and using await on processRequest() .
I went with option 4 as I enjoy using async-await syntax. Here's the solution:
// test_myFunction.js
test((`myFunction with valid path should return list of files`), async () => {
//(all of the variables established from above)
await processRequest(requestConfig)
expect(setMsgSpy).toHaveBeenCalledTimes(1);
expect(setMsgSpy)
.toHaveBeenCalledWith('loading');
expect(setButtonSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledTimes(1);
expect(setResultsSpy).toHaveBeenCalledWith(returnedFileList);
expect(setAppStateSpy).toHaveBeenCalledTimes(1);
expect(setAppStateSpy).toHaveBeenCalledWith('confirm');
});
Notice async being used on the first line and await when calling processRequest(requestConfig).

Nest JS - Issue writing Jest Test Case for a function returning Observable Axios Response

I am fairly new to NestJS + Typescript + RxJs tech stack. I am trying to write a unit test case using Jest for one of my functions but not sure if doing it correctly.
component.service.ts
public fetchComponents(queryParams) {
const url = this.prepareUrl(queryParams);
const data$ = this.httpService.get(url);
return data$
.pipe(map(({ data }) => data));
}
component.sevice.spec.ts
Test case works and passes
describe('fetchComponents', () => {
const query = {
limit: 10,
offset: 0
};
const result: AxiosResponse = {
data: 'Components',
status: 200,
statusText: 'OK',
headers: {},
config: {}
};
it('should return Dummy Data when called successfully', () => {
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementation(() => of(result));
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
}
);
});
});
Can you please provide suggestions and pointers on how exactly I should test this function. Also without using Library like marbel-rx
I am not sure if I am testing it correctly. Is there something else also which I should test?
Since Observables are asynchronous, you have to add the asynchronous done paramter and call done() after the expect that is executed last. Otherwise, jest will finish the test run after subscribe() is called without waiting for the execution of the asynchronous execution of subscribe's callback. Try to make your test fail by for example by expecting 'Komponents'. The test will not fail.
Also, I'd recommend to use mockImplementationOnce instead of mockImplementation when possible, to avoid implicitly reusing mock behaviors in later calls and therewith creating implicit dependencies.
it('should return Dummy Data when called successfully', done => {
// Add done parameter ^^^^
componentService.prepareUrl = jest.fn();
jest.spyOn(httpService, 'get').mockImplementationOnce(() => of(result));
// Prefer mockImplementationOnce ^^^^
componentService.fetchComponents(market, query)
.subscribe(
(res) => {
expect(res).toEqual('Components');
done();
// ^^^^^^ Call done() when test is finished
}
);
});

Resources