Objection.js Stubbing Chained "whereIn" methods with Sinon - node.js

Trying to stub a chained knex query using Sinon. The query looks like the following
const result = await TableModel
.query()
.whereIn('id', idList)
.whereIn('value', valueList);
Normally I use a helper function that I created that returns an instance of the model with each method stubbed to return this, like so
for (const method of ['query', 'whereIn']) {
TableModel[method] = sandbox.stub().returnsThis();
}
Then stubbing the instance within the test to resolve the necessary test case
TableModel.whereIn = sandbox.stub().resolves({ object: 'stuff' });
However, this doesn't work when chaining the same method I'm getting an error from mocha / chai / sinon that reads
TypeError: TableModel.query(...).whereIn(...).whereIn is not a function
Looking for help on how to stub and resolve the method within the test.

I've been trying to stub similar scenario:
await model.query().findById();
and I was able to stub this in following way:
const stubbedData = { ... }; // Whatever you want to get
sandbox.stub(Model, 'query').returns({
findById: sandbox.stub().returns(stubbedData),
});
In your case it will be very similar and if you need to distinguish between two whereIn then you can use withArgs or first whereIn could return another "nested" stub.
Here is a good article about stubbing complex objects:
https://codeutopia.net/blog/2016/05/23/sinon-js-quick-tip-how-to-stubmock-complex-objects-such-as-dom-objects/

Here is the unit test solution, you can use stub.withArgs(arg1[, arg2, ...]); solve this.
tableModel.js:
const TableModel = {
query() {
return this;
},
async whereIn(field, values) {
return "real data";
},
};
module.exports = TableModel;
main.js:
const TableModel = require("./tableModel");
async function main(idList, valueList) {
const result = await TableModel.query()
.whereIn("id", idList)
.whereIn("value", valueList);
return result;
}
module.exports = main;
main.test.js:
const main = require("./main");
const TableModel = require("./tableModel");
const sinon = require("sinon");
const { expect } = require("chai");
describe("50957424", () => {
afterEach(() => {
sinon.restore();
});
it("should pass", async () => {
sinon.stub(TableModel);
TableModel.query.returnsThis();
TableModel.whereIn.withArgs("id").returnsThis();
TableModel.whereIn.withArgs("value").resolves({ object: "stuff" });
const actual = await main([1, 2], ["a", "b"]);
expect(actual).to.be.eql({ object: "stuff" });
sinon.assert.calledOnce(TableModel.query);
sinon.assert.calledWith(TableModel.whereIn.firstCall, "id", [1, 2]);
sinon.assert.calledWith(TableModel.whereIn.secondCall, "value", ["a", "b"]);
});
});
Unit test result with coverage report:
50957424
✓ should pass
1 passing (16ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 92 | 100 | 66.67 | 92 | |
main.js | 100 | 100 | 100 | 100 | |
main.test.js | 100 | 100 | 100 | 100 | |
tableModel.js | 50 | 100 | 0 | 50 | 3,6 |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/50957424

Related

How do you test alternate branch of a return gaurd with jest?

Jest is telling me I have only tested 50% of the branches of my return tested. How do I test alternate branches?
const getClient = (): Collection => {
return collection(process.env.DB_COLLECTION || "Null");
};
I have tested that when I call getClient I get a collection handler. But code coverage only has 50% for the branching on the return
You could change the value of process.env.DB_COLLECTION environment variable for test case.
E.g.
index.ts:
import { collection } from './collection';
type Collection = any;
export const getClient = (): Collection => {
return collection(process.env.DB_COLLECTION || 'Null');
};
collection.ts:
export function collection(name: string) {
return 'real implementation';
}
index.test.ts:
import { getClient } from './';
import { collection } from './collection';
jest.mock('./collection');
describe('64811936', () => {
it('should pass 1', () => {
getClient();
expect(collection).toBeCalledWith('Null');
});
it('should pass 2', () => {
const DB_COLLECTION = process.env.DB_COLLECTION;
process.env.DB_COLLECTION = 'test';
getClient();
expect(collection).toBeCalledWith('test');
process.env.DB_COLLECTION = DB_COLLECTION;
});
});
unit test result:
PASS src/stackoverflow/64811936/index.test.ts
64811936
✓ should pass 1 (6ms)
✓ should pass 2 (1ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 80 | 100 | 50 | 80 | |
collection.ts | 50 | 100 | 0 | 50 | 2 |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.687s, estimated 13s

How can i globally mock lodash/deep function in jest

I am using lodash/deep function in the controller. While performing jest test for the controller it gives the following error:
All of the lodash functions gives errors.
TypeError: cloneDeep_1.default is not a function.
sample code below
import cloneDeep from 'lodash/cloneDeep'
static transformBoardBasicInfo (rawBoard:any): any {
const clonedBoard: any = cloneDeep(rawBoard) as any
clonedBoard.info = this.getInfo(rawBoard)
return clonedBoard
}
I suppose you want to add a spy on cloneDeep function. But it's unnecessary, cloneDeep function is a pure function and doesn't call any side-effected external services. So you don't need to mock or make a stub for it.
index.ts:
import cloneDeep from 'lodash/cloneDeep';
export class SomeClass {
public static transformBoardBasicInfo(rawBoard: any): any {
const clonedBoard: any = cloneDeep(rawBoard) as any;
clonedBoard.info = this.getInfo(rawBoard);
return clonedBoard;
}
public static getInfo(board) {
return '';
}
}
index.spec.ts:
import { SomeClass } from './';
import cloneDeep from 'lodash/cloneDeep';
jest.mock('lodash/cloneDeep', () => jest.fn());
describe('main', () => {
afterEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
it('should pass', () => {
jest.spyOn(SomeClass, 'getInfo');
(cloneDeep as any).mockImplementationOnce((data) => {
return require.requireActual('lodash/cloneDeep')(data);
});
const mRawBoard = { info: '123' };
const actual = SomeClass.transformBoardBasicInfo(mRawBoard);
expect(actual).toEqual({ info: '' });
expect(cloneDeep).toBeCalledWith(mRawBoard);
expect(SomeClass.getInfo).toBeCalledWith(mRawBoard);
});
});
Unit test result with coverage report:
PASS src/stackoverflow/59298693/index.spec.ts
main
✓ should pass (25ms)
----------|----------|----------|----------|----------|-------------------|
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: 5.075s, estimated 12s

Test case for node js with sinon and chai

I am trying to use sinon to do a unit test for one of my node functions.
Below is the actual function.
import { replaceYears, getFinancialYears } from './utils/years/';
const replaceAssetFileName = () => {
const years = getFinancialYears(); // return array of years [2019, 2018, 2017]
for (let i = 0; i < years.length; i++) {
replaceYears(years[i], 'NEWNAME'); // replace years just replace 2019 to FY19, don't ask why a function is written for this.
}
return true;
};
I want to mock the function getFinancialYears, so that just for test the function can return only one or two years instead of 100s of years.
I tried below test case with sinon and chai. But still I see the function “getFinancialYears” giving out the actually list of years instead of fakes.
it('We can replace file names', () => {
const stub = sinon.stub(util, 'getFinancialYears').callsFake(() => ['2019']);
expect(replaceAssetFileName()).to.be(true);
stub.restore();
}).timeout(20000);
Here is the solution to stub getFinancialYears function:
index.ts:
import { replaceYears, getFinancialYears } from './utils/years';
export const replaceAssetFileName = () => {
const years = getFinancialYears();
console.log(years);
for (let i = 0; i < years.length; i++) {
replaceYears(years[i], 'NEWNAME');
}
return true;
};
utils/years.ts:
export function getFinancialYears() {
return ['2019', '2018', '2017'];
}
export function replaceYears(year, replacer) {}
index.spec.ts:
import { replaceAssetFileName } from './';
import sinon from 'sinon';
import { expect } from 'chai';
import * as util from './utils/years';
describe('replaceAssetFileName', () => {
it('We can replace file names', () => {
const logSpy = sinon.spy(console, 'log');
const stub = sinon.stub(util, 'getFinancialYears').callsFake(() => ['2019']);
expect(replaceAssetFileName()).to.be.true;
expect(logSpy.calledWith(['2019'])).to.be.true;
stub.restore();
});
});
Unit test result with coverage report:
replaceAssetFileName
[ '2019' ]
✓ We can replace file names
1 passing (10ms)
----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files | 95.65 | 100 | 83.33 | 95.24 | |
55573978 | 100 | 100 | 100 | 100 | |
index.spec.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
55573978/utils | 66.67 | 100 | 50 | 66.67 | |
years.ts | 66.67 | 100 | 50 | 66.67 | 2 |
----------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/55573978

sinon - spy on toString method

In my file, I have something like this:
if(somevar.toString().length == 2) ....
How can I spy on toString from my test file? I know how to spy on things like parseInt with:
let spy = sinon.spy(global, 'parseInt')
But that doesn't work with toString since it's called on the value, I tried spying on Object and Object.prototype, but that doesn't work either.
You can't call sinon.spy or sinon.stub on a method of primitive value like:
sinon.spy(1, 'toString'). This is wrong.
You should call them on the Class.prototype of primitive value. Here is the unit test solution:
index.ts:
export function main(somevar: number) {
if (somevar.toString(2).length == 2) {
console.log("haha");
}
}
index.spec.ts:
import { main } from "./";
import sinon from "sinon";
import { expect } from "chai";
describe("49866123", () => {
afterEach(() => {
sinon.restore();
});
it("should log 'haha'", () => {
const a = 1;
const logSpy = sinon.spy(console, "log");
const toStringSpy = sinon.stub(Number.prototype, "toString").returns("aa");
main(a);
expect(toStringSpy.calledWith(2)).to.be.true;
expect(logSpy.calledWith("haha")).to.be.true;
});
it("should do nothing", () => {
const a = 1;
const logSpy = sinon.spy(console, "log");
const toStringSpy = sinon.stub(Number.prototype, "toString").returns("a");
main(a);
expect(toStringSpy.calledWith(2)).to.be.true;
expect(logSpy.notCalled).to.be.true;
});
});
Unit test result with 100% coverage:
49866123
haha
✓ should log 'haha'
✓ should do nothing
2 passing (28ms)
---------------|----------|----------|----------|----------|-------------------|
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 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/49866123

Rewire typescript stub private method

I have the below file to test
a.ts
export function a1(param1){
a2(param1);
return result;
}
function a2(param){
return result2;
}
I am trying to write unit test case for the exported method a1 using rewire. Below is my test class
a_test.ts
const rewiredA= rewire('a');
rewiredA.__set__('a2', (token) => 'testmessage');
// Testing exported method of a.ts
rewiredA.__get__('a1')('param');
Here instead of calling the stubbed private method which returns 'testmessage', the actual private method is getting invoked. My understanding is the mocked private method will get executed, when we invoke the exported function.
Please guide me in this.
My understanding is the mocked private method will get executed, when we invoke the exported function.
Incorrect. If a function is not exported you cannot rewire it.
Solution
Export the function you intend to rewire. You can name it _a2 to denote that it is intended to be internal.
It should work for "rewire": "^4.0.1". E.g.
a.js:
function a1(param1) {
return a2(param1);
}
function a2(param) {
return 'result2';
}
a.test.js:
const rewire = require('rewire');
const mod = rewire('./a');
const { expect } = require('chai');
describe('49504977', () => {
it('a1', (done) => {
mod.__with__(
'a2',
(token) => 'testmessage',
)(() => {
const actual = mod.__get__('a1')('param');
expect(actual).to.be.eq('testmessage');
done();
});
});
it('a2', () => {
const actual = mod.__get__('a2')();
expect(actual).to.be.eq('result2');
});
});
Unit test results with 100% coverage:
49504977
✓ a1
✓ a2
2 passing (9ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
a.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/49504977

Resources