Unit test class constructor and method with Jest - javascript

I'm trying to figure out how to perform unit tests, with Jest, on my code but I have been struggling with this. I want to be able to test the constructor and the method in my unit test but I can't understand how to do it in an efficient way.
class CommandOption {
constructor(commands) {
this.section = commands[0]
this.method = commands[1]
this.command1 = commands[2]
}
option(optionName) {
return require(`../commands/options/${optionName}`)(this)
}
}
I know that I can test the constructor fairly easily, in this case, but I don't know if it is good way or not.
const CommandOption = require('./command-option')
it('should equal "hardware", "get", and "byid"', () => {
let commandOption = new CommandOption(['hardware','get','byid'])
expect(commandOption.section).toBe('hardware')
expect(commandOption.method).toBe('get')
expect(commandOption.command1).toBe('byid')
}
I don't really know how to go about mocking the option method from there... I have read about using jest.spyOn() but I can't seem to wrap my head around it for my case... probably because I am trying to overthink it.

Unit test solution:
command-option.js:
class CommandOption {
constructor(commands) {
this.section = commands[0];
this.method = commands[1];
this.command1 = commands[2];
}
option(optionName) {
return require(`./commands/options/${optionName}`)(this);
}
}
module.exports = CommandOption;
command-option.test.js:
const CommandOption = require('./command-option');
describe('64327189', () => {
it('should equal "hardware", "get", and "byid"', () => {
let commandOption = new CommandOption(['hardware', 'get', 'byid']);
expect(commandOption.section).toBe('hardware');
expect(commandOption.method).toBe('get');
expect(commandOption.command1).toBe('byid');
});
it('should load option by name', () => {
const optionSetter = jest.fn();
jest.doMock('./commands/options/host', () => optionSetter);
let commandOption = new CommandOption(['hardware', 'get', 'byid']);
const optionName = 'host';
commandOption.option(optionName);
expect(optionSetter).toBeCalledWith(commandOption);
});
});
commands/options/host.js:
// whatever
unit test result with coverage report:
PASS src/stackoverflow/64327189/command-option.test.js
64327189
✓ should equal "hardware", "get", and "byid" (4ms)
✓ should load option by name (3ms)
-------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
command-option.js | 100 | 100 | 100 | 100 | |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.848s, estimated 10s

Related

Unit testing Hapi Server Methods

I have an app that's making heavy use of Hapi's Server Methods. The methods are being applied via an exported register function, and the code that's being executed for the method is in the same file and thus not exported. I'm trying to write tests for these methods without exporting the functions that they call in the simplest possible way, but I haven't found any examples of how to do so.
export function register(server) {
server.method(
'methodNameA',
async (path, {}) => {
// Some code here
return something; // I want to test this result
},
{
cache: methodCache.halfDay,
generateKey(path, { ... } = {}) {
return something;
},
}
);
};
Abstracting that logic is an option, but I'd rather not expose it just for a test. I'd also prefer to not test an entire route just to validate this bit of logic (though that may be the ultimate solution here).
I will use jestjs as my unit testing framework. You can provide a mocked server and a mocked implementation for server.method(). Then you can get the original method in your test case.
After getting the original method, test it as usual.
E.g.
register.ts:
export function register(server) {
server.method('methodNameA', async () => {
return 'something';
});
}
register.test.ts:
import { register } from './register';
describe('67093784', () => {
it('should pass', async () => {
let methodNameA;
const mServer = {
method: function mockImplementation(m, func) {
methodNameA = func;
},
};
register(mServer);
// test method
const actual = await methodNameA();
expect(actual).toEqual('something');
});
});
unit test result:
PASS examples/67093784/register.test.ts (11.981 s)
67093784
✓ should pass (3 ms)
-------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
register.ts | 100 | 100 | 100 | 100 |
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.766 s

jest mocking and setting up default behavior

There is a function I need to unit test:
const readiness = require("./readiness");
function stopProcessNewRequests(){
readiness.setNotReady();
}
Here is my "readiness" file:
const state = require("./state");
const hooks = require("./hooks");
function setReady(){
state.ready = true;
hooks.notifyHook(state.ready);
}
function setNotReady(){
state.ready = false;
hooks.notifyHook(state.ready);
}
module.exports = {
setReady, setNotReady
};
and finally, the state.js file:
exports.ready = false;
exports.pendingRequests = {};
exports.changeStatusHooks = [];
exports.requestsInProcessNum = 0;
exports.authClient = null;
exports.webOperationsClient = null;
exports.webQueryClient = null;
As you can see, there are multiple chained imports, how do I mock them? I need my state file to be of certain values in order to check if it actually changes.
Here's what I have, but state does not seem to change, and the test fails.
describe('Testing processing new requests:', ()=> {
test('should stop processing new requests:', ()=> {
// jest.mock('../lib/grpc/readiness',);
jest.mock('../lib/grpc/state');
const state = require("../lib/grpc/state");
const { stopProcessNewRequests } = require('../lib/grpc/requestsManager');
state.ready = true;
stopProcessNewRequests();
expect(state.ready).toBeFalsy();
})
})
It is best not to test across modules. Which means for testing requestsManager module, you should mock readiness module, not indirectly module state and hooks. Besides, jest.mock should NOT be used in function scope, it should be used in module scope.
E.g.
readiness.js:
const state = require('./state');
const hooks = require('./hooks');
function setReady() {
state.ready = true;
hooks.notifyHook(state.ready);
}
function setNotReady() {
state.ready = false;
hooks.notifyHook(state.ready);
}
module.exports = {
setReady,
setNotReady,
};
hooks.js:
function notifyHook() {}
module.exports = { notifyHook };
requestsManager.js:
const readiness = require('./readiness');
function stopProcessNewRequests() {
readiness.setNotReady();
}
module.exports = { stopProcessNewRequests };
state.js:
exports.ready = false;
exports.pendingRequests = {};
exports.changeStatusHooks = [];
exports.requestsInProcessNum = 0;
exports.authClient = null;
exports.webOperationsClient = null;
exports.webQueryClient = null;
Below are test files:
requestsManager.test.js:
const { stopProcessNewRequests } = require('./requestsManager');
const readiness = require('./readiness');
describe('62057531', () => {
it('should pass', () => {
const setNotReadyMock = jest.spyOn(readiness, 'setNotReady').mockReturnValueOnce();
stopProcessNewRequests();
expect(setNotReadyMock).toBeCalledTimes(1);
});
});
readiness.test.js:
const { setNotReady, setReady } = require('./readiness');
const hooks = require('./hooks');
const state = require('./state');
describe('62057531', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should set state to not ready', () => {
jest.spyOn(hooks, 'notifyHook').mockReturnValueOnce();
setNotReady();
expect(hooks.notifyHook).toBeCalledWith(false);
expect(state.ready).toBeFalsy();
});
it('should set state to ready', () => {
jest.spyOn(hooks, 'notifyHook').mockReturnValueOnce();
setReady();
expect(hooks.notifyHook).toBeCalledWith(true);
expect(state.ready).toBeTruthy();
});
});
Unit test results with coverage report:
PASS stackoverflow/62057531/requestsManager.test.js (14.294s)
62057531
✓ should pass (6ms)
PASS stackoverflow/62057531/readiness.test.js
62057531
✓ should set state to not ready (5ms)
✓ should set state to ready (1ms)
--------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 75 | 100 |
hooks.js | 100 | 100 | 0 | 100 |
readiness.js | 100 | 100 | 100 | 100 |
requestsManager.js | 100 | 100 | 100 | 100 |
state.js | 100 | 100 | 100 | 100 |
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 2 passed, 2 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 17.288s, estimated 21s

Mock the return value of an imported function in Typescript with Jest

I have a function, lets call it generateName, which as you’ve guessed it, generates a name. The problem is that a new name is generated each time time a test is ran.
In one of my tests, I assert that a function is called with an object containing this name. However, the name keeps on changing. I could just check that the object has property name, but I don’t really want to do that.
My idea is that I can mock the return value of the generateName function and do something like this
Import { generateName } from ‘libs/generateName’
jest.fn(generateName).mockResolvedValue ( ‘hello’ )
expect ( spy ).toHaveBeenCalledWith (
expect.objectContaining ( {
name: 'houses',
} )
)
You can use jest.mock(moduleName, factory, options) to mock libs/generateName module.
E.g.
generateName.ts:
export async function generateName() {
const name = Math.random() + '';
return name;
}
main.ts:
import { generateName } from './generateName';
export function main() {
return generateName();
}
main.test.ts:
import { main } from './main';
import { generateName } from './generateName';
jest.mock('./generateName', () => {
return {
generateName: jest.fn(),
};
});
describe('61350152', () => {
it('should pass', async () => {
(generateName as jest.MockedFunction<typeof generateName>).mockResolvedValueOnce('hello');
const actual = await main();
expect(actual).toBe('hello');
});
});
unit test results with coverage report:
PASS stackoverflow/61350152/main.test.ts (28.524s)
61350152
✓ should pass (6ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
main.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 31.98s

Angular - Jest unit testing of method with parameter

I need to unit test the following method which takes one parameter. But I am unable to test.
Component Method:
resetAdd(qa: qaModel) {
const street = qa.children.find(temp => temp.token === 'street');
const zip = qa.children.find(temp => temp.token === 'zip');
const city = qa.children.find(temp => temp.token === 'city');
this.fGroup.controls[street.pathway].callReset();
this.fGroup.controls[zip.pathway].callReset();
this.fGroup.controls[city.pathway].callReset();
}
TestFile:
it('resetAdd Method is called', () => {
const param1= jest.fn();
expect(component.resetAdd).toHaveBeenCalledWith(param1);
});
I am not sure what's wrong and also please let me know what else test case I can write.
Here is the unit test solution, you can use jest.spyOn:
By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries.
That means when you can jest.spyOn to spy on a method of object without custom implementation, the original method of this object will be executed as usual. Beside, there is a spy on the method, so you can use assert of jest to check if it is executed or not. For your case, the method is callReset.
index.ts:
type qaModel = any;
export class SomeComponent {
private fGroup = {
controls: {
street: {
callReset() {}
},
zip: {
callReset() {}
},
city: {
callReset() {}
}
}
};
resetAdd(qa: qaModel) {
const street = qa.children.find(temp => temp.token === 'street');
const zip = qa.children.find(temp => temp.token === 'zip');
const city = qa.children.find(temp => temp.token === 'city');
this.fGroup.controls[street.pathway].callReset();
this.fGroup.controls[zip.pathway].callReset();
this.fGroup.controls[city.pathway].callReset();
}
}
index.spec.ts:
import { SomeComponent } from './';
describe('SomeComponent', () => {
it('should call resetAdd', () => {
const comp = new SomeComponent();
const streetCallResetSpy = jest.spyOn(comp['fGroup']['controls']['street'], 'callReset');
const zipCallResetSpy = jest.spyOn(comp['fGroup']['controls']['zip'], 'callReset');
const cityCallResetSpy = jest.spyOn(comp['fGroup']['controls']['city'], 'callReset');
const qaModel = {
children: [
{ token: 'street', pathway: 'street' },
{ token: 'zip', pathway: 'zip' },
{ token: 'city', pathway: 'city' }
]
};
comp.resetAdd(qaModel);
expect(streetCallResetSpy).toBeCalledTimes(1);
expect(zipCallResetSpy).toBeCalledTimes(1);
expect(cityCallResetSpy).toBeCalledTimes(1);
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58818402/index.spec.ts
SomeComponent
✓ should call resetAdd (5ms)
----------|----------|----------|----------|----------|-------------------|
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: 3.612s, estimated 8s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58818402
You are not calling resetAdd method in unit test, still expecting it should have been called.
Steps are :
Spy method which you want to be called,
call that method
then check for to have been called
it('resetAdd Method is called', () => {
const param: qaModel = null // define param of type which is expected. I dont know structure of qaModel, so for example put as null
spyOn(component, 'resetAdd') // spy first
component.resetAdd(param)
expect(component.resetAdd).toHaveBeenCalledWith(param);});

How do unit test on 'window.open' (javascript) using sinon

this is the code I have to test:
myFunction: function(data) {
var file = new Blob(data, {type: 'text/plain'});
window.open(window.URL.createObjectURL(file));
}
In order to test it, I thought to test if the window.open function is called, applying a 'spy' on window.open , in the following way:
sandbox.spy(window, 'open');
but, even leaving previous line as the unique line in the test, what I only get is the test failure and the following message:
global leaks detected: consoleLogging, open
Thus, in order to avoid that, I tried to re-define the function in the test in this way:
global.window = {
open: function (url) {}
};
In this case an exception raised:
Attempted to assign to readonly property
Then I tried to mock the 'open' via the following:
sandbox.mock(window, 'open');
objectUnderTest.myFunction();
expect(window.open.callCount).to.equal(1);
this way, I get no global or readonly errors, but an exception on the 'expect', telling that:
expected undefined to equal 1
Does someone knows a way to successfully test window.open?
Here is the unit test solution based on Node.js environment:
index.js:
const obj = {
myFunction: function(data) {
var file = new Blob(data, { type: 'text/plain' });
window.open(window.URL.createObjectURL(file));
}
};
module.exports = obj;
index.spec.js:
const obj = require('./');
const sinon = require('sinon');
const { expect } = require('chai');
describe('53524524', () => {
before(() => {
class Blob {}
global.Blob = Blob;
global.window = {
open() {},
URL: {
createObjectURL() {}
}
};
});
it('should test myFunction correctly', () => {
const openStub = sinon.stub(window, 'open');
const createObjectURLStub = sinon.stub(global.window.URL, 'createObjectURL').returns('fake object url');
obj.myFunction('fake data');
expect(createObjectURLStub.calledOnce).to.be.true;
expect(openStub.calledWith('fake object url')).to.be.true;
});
});
Unit test result with coverage report:
53524524
✓ should test myFunction correctly
1 passing (11ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 66.67 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
index.spec.js | 100 | 100 | 60 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/53524524

Categories

Resources