I have a very complex object i am using to mock out a DataTable() object for the purposes of testing.
const obj = {
DataTable: () => {
return {
columns: () => {
return {
data: () => {
return {
eq: () => {
return {
indexOf: jest.fn(),
};
},
};
},
visible: () => {
return jest.fn();
},
};
},
};
},
};
Inside my testing code I am trying to spy on some of these functions but it always returns undefined. Is there a way to mock out the return value of deeply nested functions?
jest.spyOn(obj.DataTable().columns().data().eq(), 'indexOf').mockReturnValue('test');
console.log(obj.DataTable().columns().data().eq().indexOf()); // returns undefined, should return 'test'
Here is the unit test solution:
index.ts:
import { obj } from './obj';
export function main() {
return obj.DataTable().columns().data().eq().indexOf();
}
obj.ts:
export const obj = {
DataTable: () => {
return {
columns: () => {
return {
data: () => {
return {
eq: () => {
return {
indexOf: () => 'real data',
};
},
};
},
};
},
};
},
};
index.test.ts:
import { main } from './';
import { obj } from './obj';
describe('61396089', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should pass', () => {
const mockDataTable = {
columns: jest.fn().mockReturnThis(),
data: jest.fn().mockReturnThis(),
eq: jest.fn().mockReturnThis(),
indexOf: jest.fn().mockReturnValueOnce('fake data'),
};
jest.spyOn(obj, 'DataTable').mockImplementationOnce(() => mockDataTable);
const actual = main();
expect(actual).toBe('fake data');
expect(mockDataTable.columns).toBeCalledTimes(1);
expect(mockDataTable.data).toBeCalledTimes(1);
expect(mockDataTable.eq).toBeCalledTimes(1);
expect(mockDataTable.indexOf).toBeCalledTimes(1);
});
});
unit test results with coverage report:
PASS stackoverflow/61396089/index.test.ts (20.978s)
61396089
✓ should pass (8ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 44.44 | 100 | 16.67 | 44.44 |
index.ts | 100 | 100 | 100 | 100 |
obj.ts | 16.67 | 100 | 0 | 16.67 | 3-10
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 23.889s
source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/61396089
Related
// utils.js
var someModule = require('someModule');
someModule.setKey('API_KEY');
I want to test setKey function. So I wrote below unit test case.
jest.mock('someModule, () => {
return {
setKey: jest.fn()
}
})
describe('utils', () => {
afterEach(()=> {
jest.clearAllMocks()
})
it(`test case 1`, () => {})
it(`test case utils`, () => {
expect(someModule.setKey.mocks.calls).toHaveLength(1)
})
});
Last test case fails, but if I move the last test case as first then it works. Since there is no clearAllMocks function executed.
What should be a good way to test it?
It should work. Here is an example:
utils.js:
const someModule = require('someModule');
function main() {
someModule.setKey('API_KEY');
}
module.exports = main;
Since the someModule is not a real module, so I use {virtual: true} option.
utils.test.js:
const main = require('./utils');
const someModule = require('someModule');
jest.mock(
'someModule',
() => {
return { setKey: jest.fn() };
},
{ virtual: true },
);
describe('60192332', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('should set key', () => {
main();
expect(someModule.setKey.mock.calls).toHaveLength(1);
});
});
Unit test results with 100% coverage:
PASS stackoverflow/60192332/utils.test.js (6.732s)
60192332
✓ should set key (5ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
utils.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.38s, estimated 13s
I am new to Jest and i am trying test async await. And I am trying to cover Json.Parse and it is throwing an exception like below mentioned.
Error Message
SyntaxError: Unexpected token o in JSON at position 1
at JSON.parse (<anonymous>)
Code
public async callDataSourceCommand(dialogData: any, RecipeId: string) {
const gridItems = await this.dataSourceService.myPromiseMethod(id, collection);
this.updateGrid(JSON.parse(gridItems));
}
private updateGrid(gridItems: any) {}
Mock Data
public get dataSourceServiceMock(): any = {
return {
myPromiseMethod: function () {
return Promise.resolve({
selectedOrder: {
earlierStartTime: '2/5/2020',
__id: 'orderId123'
},
Collection: [{
__id: 'b1order 1',
resName: 'New recipe_V1.0',
plannedQuantity: '3',
resId: 'ns=6;s=4/ProjectData/1',
actualQuantity: '1',
description: 'batchDesc',
}]
});
}
}
}
Test Suite
it('1. Should execute ', async() => {
const myDialogApp: DialogApp = TestBed.get(DialogApp);
myDialogApp.selectedOrder = selectedOrder;
myDialogApp.RecipeId = Recipe.__id;
jest.spyOn(dataSourceServiceMock, 'myPromiseMethod');
await myDialogApp.callDataSourceCommand(dialogData, RecipeId);
expect(dataSourceServiceMock.myPromiseMethod).toHaveBeenCalled();
});
Well, you can simply stub the response and pass it as below
public get dataSourceServiceMock(): any = {
return {
myPromiseMethod: function () {
return Promise.resolve(JSON.stringify({
selectedOrder: {
earlierStartTime: '2/5/2020',
__id: 'orderId123'
},
Collection: [{
__id: 'b1order 1',
resName: 'New recipe_V1.0',
plannedQuantity: '3',
resId: 'ns=6;s=4/ProjectData/1',
actualQuantity: '1',
description: 'batchDesc',
}]
}));
}
}
}
Hope this helps!
Here is the unit test solution:
index.ts:
class Service {
private dataSourceService;
constructor(dataSourceService) {
this.dataSourceService = dataSourceService;
}
public async callDataSourceCommand(dialogData: any, RecipeId: string) {
const id = '1';
const collection = [];
const gridItems = await this.dataSourceService.myPromiseMethod(id, collection);
this.updateGrid(JSON.parse(gridItems));
}
private updateGrid(gridItems: any) {}
}
export { Service };
datasourceService.ts:
class DataSourceService {
public myPromiseMethod(id, collection) {
return JSON.stringify({});
}
}
export { DataSourceService };
index.test.ts:
import { Service } from './';
describe('60446313', () => {
it('should pass', async () => {
const gridItemsMock = JSON.stringify({
selectedOrder: {
earlierStartTime: '2/5/2020',
__id: 'orderId123',
},
Collection: [
{
__id: 'b1order 1',
resName: 'New recipe_V1.0',
plannedQuantity: '3',
resId: 'ns=6;s=4/ProjectData/1',
actualQuantity: '1',
description: 'batchDesc',
},
],
});
const dataSourceServiceMock = {
myPromiseMethod: jest.fn().mockResolvedValueOnce(gridItemsMock),
};
const parseSpy = jest.spyOn(JSON, 'parse');
const service = new Service(dataSourceServiceMock);
await service.callDataSourceCommand('dialogData', '1');
expect(dataSourceServiceMock.myPromiseMethod).toBeCalledWith('1', []);
expect(parseSpy).toBeCalledWith(gridItemsMock);
});
});
Unit test results with coverage report:
PASS stackoverflow/60446313/index.test.ts
60446313
✓ should pass (9ms)
----------|---------|----------|---------|---------|-------------------
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: 4.509s, estimated 6s
I am calling $('.all-products-tab-buttons .row').scroll() method in my js file. But can't find out how to test this method using JEST.
Full code is below:
$('.all-products-tab-buttons .row').scroll(event => {
let $width = $('.all-products-tab-buttons .row').outerWidth();
let $scrollWidth = $('.all-products-tab-buttons .row')[0].scrollWidth;
let $scrollLeft = $('.all-products-tab-buttons .row').scrollLeft();
if ($scrollWidth - $width === $scrollLeft) {
$('.all-products-tab-buttons').addClass('remove');
} else {
$('.all-products-tab-buttons').removeClass('remove');
}
});
How may I test if and else conditions also?
Here is the unit test solution:
index.js:
import $ from 'jquery';
function main() {
$('.all-products-tab-buttons .row').scroll((event) => {
let $width = $('.all-products-tab-buttons .row').outerWidth();
let $scrollWidth = $('.all-products-tab-buttons .row')[0].scrollWidth;
let $scrollLeft = $('.all-products-tab-buttons .row').scrollLeft();
if ($scrollWidth - $width === $scrollLeft) {
$('.all-products-tab-buttons').addClass('remove');
} else {
$('.all-products-tab-buttons').removeClass('remove');
}
});
}
export default main;
index.test.js:
import main from './';
import $ from 'jquery';
jest.mock('jquery', () => jest.fn());
describe('60003884', () => {
it('should add class', () => {
const tabButtonRows = [{ scrollWidth: 200 }];
tabButtonRows.constructor.prototype.scroll = jest.fn().mockImplementationOnce((handler) => {
handler();
});
tabButtonRows.constructor.prototype.outerWidth = jest.fn().mockReturnValueOnce(100);
tabButtonRows.constructor.prototype.scrollLeft = jest.fn().mockReturnValueOnce(100);
const tabButtons = { addClass: jest.fn(), removeClass: jest.fn() };
$.mockImplementation((selector) => {
switch (selector) {
case '.all-products-tab-buttons .row':
return tabButtonRows;
case '.all-products-tab-buttons':
return tabButtons;
}
});
main();
expect(tabButtons.addClass).toBeCalledWith('remove');
});
it('should remove class', () => {
const tabButtonRows = [{ scrollWidth: 200 }];
tabButtonRows.constructor.prototype.scroll = jest.fn().mockImplementationOnce((handler) => {
handler();
});
tabButtonRows.constructor.prototype.outerWidth = jest.fn().mockReturnValueOnce(100);
tabButtonRows.constructor.prototype.scrollLeft = jest.fn().mockReturnValueOnce(50);
const tabButtons = { addClass: jest.fn(), removeClass: jest.fn() };
$.mockImplementation((selector) => {
switch (selector) {
case '.all-products-tab-buttons .row':
return tabButtonRows;
case '.all-products-tab-buttons':
return tabButtons;
}
});
main();
expect(tabButtons.removeClass).toBeCalledWith('remove');
});
});
Unit test results with 100% coverage:
PASS src/stackoverflow/60003884/index.test.js (14.309s)
60003884
✓ should add class (7ms)
✓ should remove class (1ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 16.079s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/60003884
I have the following file I would like to test
functions.js:
function funcOne() {
return funcTwo();
}
function funcTwo() {
return 'func two';
}
module.exports = {
funcOne,
funcTwo
};
I want to mock the funcTwo so that it returns me different string when called through my functions.funcOne
functions.test.js:
function mockFunctions() {
const original = require.requireActual('./functions');
return {
...original, // Pass down all the exported objects
funcTwo: jest.fn(() => {
return 'my test func';
})
};
}
jest.mock('./functions', () => mockFunctions());
const functions = require.requireMock('./functions');
it('renders without crashing', () => {
console.log(functions.funcOne());
});
It still prints me 'func two' instead of 'my test func'.
Is there a way to leave the module intact and just mock one particular method?
Here is a solution:
functions.js:
function funcOne() {
return exports.funcTwo();
}
function funcTwo() {
return 'func two';
}
exports.funcOne = funcOne;
exports.funcTwo = funcTwo;
Unit test:
functions.spec.js
const functions = require('./functions');
describe('functions test suites', () => {
it('funcOne', () => {
const funcTwoSpyOn = jest.spyOn(functions, 'funcTwo').mockReturnValueOnce('my test func');
const actualValue = functions.funcOne();
expect(actualValue).toBe('my test func');
expect(funcTwoSpyOn).toBeCalledTimes(1);
funcTwoSpyOn.mockRestore();
});
it('funcTwo', () => {
const actualValue = functions.funcTwo();
expect(actualValue).toBe('func two');
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/53889291/functions.spec.js
functions test suites
✓ funcOne (5ms)
✓ funcTwo
--------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
functions.js | 100 | 100 | 100 | 100 | |
--------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.199s
Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/53889291
Below is a very simple jest unit test and when running it, you will get error like
Cannot spyOn on a primitive value; undefined given
TypeError: Cannot read property 'getItem' of undefined
but according to the last two comments of this post, localStorage and sessionStorage were already added to latest JSDOM and jest. If using jest-localstorage-mock and add it to my jest setupFiles then you will see weird error like
TypeError: object[methodName].mockImplementation is not a function
So my question is what's the best way to mock localStorage/sessionStorage in jest. Thanks
describe('window.sessionStorage', () => {
let mockSessionStorage;
beforeEach(() => {
mockSessionStorage = {};
jest.spyOn(window.sessionStorage, "getItem").mockImplementation(key => {
return mockSessionStorage[key];
});
});
describe('getItem-', () => {
beforeEach(() => {
mockSessionStorage = {
foo: 'bar',
}
});
it('gets string item', () => {
const ret = window.sessionStorage.getItem('foo');
expect(ret).toBe('bar');
});
});
});
Below is my jest config
module.exports = {
verbose: true,
//setupFiles: ["jest-localstorage-mock"],
testURL: "http://localhost/"
};
Here is the solution only use jestjs and typescript, nothing more.
index.ts:
export function getUserInfo() {
const userInfo = window.sessionStorage.getItem('userInfo');
if (userInfo) {
return JSON.parse(userInfo);
}
return {};
}
index.spec.ts:
import { getUserInfo } from './';
const localStorageMock = (() => {
let store = {};
return {
getItem(key) {
return store[key] || null;
},
setItem(key, value) {
store[key] = value.toString();
},
removeItem(key) {
delete store[key];
},
clear() {
store = {};
}
};
})();
Object.defineProperty(window, 'sessionStorage', {
value: localStorageMock
});
describe('getUserInfo', () => {
beforeEach(() => {
window.sessionStorage.clear();
jest.restoreAllMocks();
});
it('should get user info from session storage', () => {
const getItemSpy = jest.spyOn(window.sessionStorage, 'getItem');
window.sessionStorage.setItem('userInfo', JSON.stringify({ userId: 1, userEmail: 'example#gmail.com' }));
const actualValue = getUserInfo();
expect(actualValue).toEqual({ userId: 1, userEmail: 'example#gmail.com' });
expect(getItemSpy).toBeCalledWith('userInfo');
});
it('should get empty object if no user info in session storage', () => {
const getItemSpy = jest.spyOn(window.sessionStorage, 'getItem');
const actualValue = getUserInfo();
expect(actualValue).toEqual({});
expect(window.sessionStorage.getItem).toBeCalledWith('userInfo');
expect(getItemSpy).toBeCalledWith('userInfo');
});
});
Unit test result with 100% coverage report:
PASS src/stackoverflow/51566816/index.spec.ts
getUserInfo
✓ should get user info from session storage (6ms)
✓ should get empty object if no user info in session storage (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: 4.548s, estimated 6s
Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/51566816
You probably do not even need a mock. Just use window.sessionStorage as usual and write your condition based on window.sessionStorage.getItem(...) result instead of spying window.sessionStorage.setItem . Simply don't forget to call window.sessionStorage.clear() in beforeEach as demonstrated.
From Eric Burel's comment
This worked for me in the context of using jest:
beforeAll(() =>
sessionStorage.setItem(
KeyStorage.KEY_NAME,
JSON.stringify([Permission.VALUE_])
)
);
afterAll(() =>
sessionStorage.removeItem(KeyStorage.CONTEXT_TYPE_GLOBAL_PERMISSIONS)
);
This works for me along with adding object:
defineProperty(window, 'sessionStorage', {
writable: true,
configurable: true,
value: localStorageMock
}