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
Related
I have a class called call.js which has a toggleAudio function. I am trying to mock this function so I can use it in my tests. The code is as follows:
My test:
test('toggleAudio function', () => {
toggleAudio = require('./call');
jest.mock('./call')
expect(toggleAudio(true)).toBe(true);
});
My mocked file inside mocks/call.js
function toggleAudio(b) {
return b;
};
Getting error:
TypeError: toggleAudio is not a function
13 | toggleAudio = require('./call');
14 | jest.mock('./call')
> 15 | expect(toggleAudio()).toBe(true);
| ^
16 | });
17 |
The issue was with the extra white space between the function name and body..
function toggleAudio(b){
return b;
};
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
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
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
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