Unit Testing JS - using JEST - javascript

Beginner Level - Unit Testing
Here's the function
export function toHex(number) {
const nstr = number.toString(16);
if (nstr.length % 2) {
return `0${nstr}`;
}
return nstr;
Able to figure out if the function is true or not by running the following Unit testing code:
// testing new function
describe('toHex', () => { //mocking the method
test('Testing Function toHex ', () => { // declaring the method
const str = 14 // initial value
const actual = toHex(str) // calculate the value
expect(actual).toMatchSnapshot(); // checking whether is true
})
});
Now how could I add different scenarios and make the following function to pass / Fail
Thanks

Related

Javascript New Function - Best way to pass large number of functions as parameter

In angular project and learning to build a feature to run a custom script using the new function method. With the great help from this forum I have been able to come up with core script solution.
The question I have if I have a large number of custom functions I need to pass into New Function to access what would be the best method?
So far I have 2 options:
Option 1.
Passing each function as parameter. My concern if there is 50 plus function this could look messy.
eg
const userFunction = new Function('testFunc1','testFunc2','testFunc3'...'testFunc50', script);
Option 2.
Nest the functions in parent function. The draw back is to access the function we need to call the parent function which again can look messy in the code writing. Eg
parentFunc().nestFunc().
Question is there a better way to do this?
Code and stackblitz below.
https://stackblitz.com/edit/angular-bx5kia?file=src/main.ts
option1() {
var nestFunc1 = () => {
alert('Nest1');
};
var nestFunc2 = () => {
alert('Nest2');
};
const script = `
var value = 1 +4;\n
console.log('value',value);\n
nestFunc1();\n
console.log("End Script");\n
`;
const userFunction = new Function('nestFunc1', 'nestFunc2', script);
userFunction(nestFunc1, nestFunc2);
}
option2() {
const script = `
var value = 1 +4;\n
console.log('value',value);\n
parentFunc().nestFunc2();\n
console.log("End Script");\n
`;
var parentFunc = (msg?: string) => {
console.log('Test Function', msg);
var nestFunc1 = () => {
alert('Nest 1');
};
var nestFunc2 = () => {
alert('Nest 2');
};
return { nestFunc1, nestFunc2 };
};
const userFunction = new Function('parentFunc', script);
userFunction(parentFunc);
}
Well, I'm not an expert with Angular but as we are working with JS maybe I can give you some light in plain JS and then you can convert it to Angular syntax.
In JS you can write functions inside an array:
const functions = [
() => console.log( 'Nest 1' ),
() => console.log( 'Nest 2' )
]
And then, in your userFunction you can make a loop inside this array calling all functions:
const userFunction = ( functionsArray ) => {
functionsArray.forEach( nestedFunction => nestedFunction() )
}
Hope I can give you any idea :)

My spy function is not getting called - how can I properly spy this function?

I'm trying to do call a function that is imported as a function independently in another function that I'm calling from my unit test. How can I get callcount of 1 on the functionIWantToSpy in this scenario?
auditEvent.js:
const { verify } = require('#mycompany/verifylib');
const { functionIWantToSpy } = require('#mycompany/another-lib');
const auditEvent = () => {
verify();
functionIWantToSpy();
};
module.exports = { auditEvent };
test:
const { verify } = require('#mycompany/verify-lib');
const { functionIWantToSpy } = require('#mycompany/another-lib');
describe('mytest', () => {
let spiedFuntion;
let verifyStub;
beforeEach(() => {
verifyStub = sinon.stub();
({auditEvent} = proxyquire('./auditEvent', {
'#mycompny/verify-lib': {
verify: verifyStub,
'#noCallThru': true,
},
}));
spiedFunction = sinon.spy(functionIWantToSpy);
});
it('should work'), async () => {
auditEvent();
expect(functionIWantToSpy).to.have.callCount(1); // Getting callcount of 0 here...
});
});
Spying involves replacing the function with a new function. You are replacing what is referred to by the identifier functionIWantToSpy so that it refers to a new spy function, instead of the original function referred to by require('#mycompany/another-lib').functionIWantToSpy. The code inside the module can't see your new spy function. The expression require('#mycompany/another-lib').functionIWantToSpy refers to the original function, unchanged.
Because require caches results (i.e., only the first require("foo") executes foo, and any subsequent require("foo") summons up that same object returned by the first require call), you can modify the functionIWantToSpy method of the require('#mycompany/another-lib') object, using the two-argument form of sinon.spy:
spiedFunction = sinon.spy(require('#mycompany/another-lib'), "functionIWantToSpy");
You must do this before the property is ever accessed (and value stored) by the module being tested:
verifyStub = sinon.stub();
// IMPORANT: FIRST, spy the functionIWantToSpy property on the required object before importing auditEvent
spiedFunction = sinon.spy(require('#mycompany/another-lib'), "functionIWantToSpy");
({auditEvent} = proxyquire('./auditEvent', {
'#mycompny/verify-lib': {
verify: verifyStub,
'#noCallThru': true,
},
}));
This should work because when the auditEvent module runs for the first time and gets to
const { functionIWantToSpy } = require('#mycompany/another-lib');
then require('#mycompany/another-lib').functionIWantToSpy will refer to the spy function.

Jest - how to check if a function is called by another function

I'm using Jest to test some Javascript. In a file, called 'function.js' I have two functions like so:
functions.js
const firstFunc = () => {
const x = secondFunc()
return x
}
const secondFunc = () => {
return '5'
}
module.exports = {
firstFunc,
secondFunc
}
In my Jest test file I have:
const functions = require('./functions.js')
test('test if secondFunc is called', () => {
const mockFunction = jest.fn()
functions.secondFunc = mockFunction;
functions.firstFunc()
expect(mockFunction).toHaveBeenCalled()
})
I've actually tried many different variations of things like this. I'm unable to get jest.fn() to work I want it to. Overall I'm wanting to be able to see information about secondFunc, such as how many times it was called, what parameters with, etc, but I have to actually call firstFunc. Can anyone help me as I can't seem to figure this out.
You need to do some refactoring. Keep the same reference for secondFunc.
Then you can replace it with a mocked object.
functions.js:
const firstFunc = () => {
const x = exports.secondFunc();
return x;
};
const secondFunc = () => {
return '5';
};
exports.firstFunc = firstFunc;
exports.secondFunc = secondFunc;
unit test result:
PASS stackoverflow/62583841/functions.test.js (12.17s)
✓ test if secondFunc is called (3ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 13.779s

How to pass integer values in cucumber test and verify the result

How do I call a simple addition function and assert the result of two values using selenium-cucumber-js framework with a test written below. While running the below it says
TypeError: TypeError: Cannot read property 'addvalues' of undefined
at createWorld.When (C:\Tests\cucumber\step-definitions\addvalues-steps.js:5:25)
Feature:
Scenario: Addition of two values
When Add two values 5 and 10
Then I should get result 15
// Here is my 'addvalues-steps.js' file
const expect = require('chai').expect;
module.exports = function () {
this.When(/^Add two values (-?\d+) and (-?\d+)$/, (x, y) =>{
this.page.addvalues.addValues(x,y);
})
this.Then(/^I should get result (-?\d+)$/, (ans) =>{
let tot = this.page.addvalues.addValues(x, y);
expect(tot).to.be.eql(ans);
})
};
// Following is my 'addvalues.js file'
module.exports = {
addValues(x,y){
var total = x + y ;
return total ;
}
};
// world.js >>
const { CustomWorld } = require('cucumber')
function CustomWorld() {
console.log('overriding the world')
this.page = {
addvalues: require('../page-objects/addvalues')
}
console.log("This is the recent error log:"+this.page.addvalues)
}
module.exports = function() {
this.World = CustomWorld;
Note: the below example is for an old version of cucumber-js: 1.3.3.
With cucumber.js, when you're referencing this from inside step definitions, you're actually referencing the World context. So, for this.page.addvalues.addValues(x,y); to work properly, you'll first need to create page that has a reference to your addvalues.js. Something along these lines:
world.js:
function CustomWorld() {
console.log('overriding the world')
this.page = {
addvalues: require('../page-objects/addvalues')
}
}
module.exports = function() {
this.World = CustomWorld;
};
addvalues.js:
//addvalues.js
module.exports = {
addValues(x,y){
var total = x + y ;
return total ;
}
};
There's also a couple of things to correct in your steps.js.
Don't pass arrow functions into the steps, as this will remove the this context that you're setting in World.js.
If you want to share variables between steps (as you do in your example), you need to store them somewhere. One such place, again, would be the World context. Note how in my version I set this.prevResult
When the variables are injected into your steps, they are injected as strings. Note the parseInt() in my version.
addvalues-steps.js:
const expect = require('chai').expect;
module.exports = function() {
this.When(/^Add two values (-?\d+) and (-?\d+)$/, function (x, y) {
this.prevResult = this.page.addvalues.addValues(parseInt(x, 10), parseInt(y, 10));
})
this.Then(/^I should get result (-?\d+)$/, function (ans) {
let tot = this.prevResult;
expect(tot).to.be.eql(parseInt(ans, 10));
})
}
UPD: It turns out that the question is about selenium-cucumber-js, which is a framework on top of cucumber-js. Disregard the comments about the world.js.
According to selenium-cucumber-js docs, you don't need this to access the page objects in your step definitions:
Page objects are accessible via a global page object and are
automatically loaded from ./page-objects.
const expect = require('chai').expect;
module.exports = function() {
this.When(/^Add two values (-?\d+) and (-?\d+)$/, function (x, y) {
this.prevResult = page.addvalues.addValues(parseInt(x, 10), parseInt(y, 10));
})
this.Then(/^I should get result (-?\d+)$/, function (ans) {
let tot = this.prevResult;
expect(tot).to.be.eql(parseInt(ans, 10));
})
}

How to spy on static generator functions?

I have an utility function that exposes a generator:
export class Utility {
// provides a generator that streams 2^n binary combinations for n variables
public static *binaryCombinationGenerator(numVars: number): IterableIterator<boolean[]> {
for (let i = 0; i < Math.pow(2, numVars); i++) {
const c = [];
//fill up c
yield c;
}
}
}
Now, I am using this generator in my code as follows:
myFuncion(input){
const n = numberOfVariables(input);
const binaryCombinations = Utility.binaryCombinationGenerator(n);
let combination: boolean[] = binaryCombinations.next().value;
while (till termination condition is met) {
// do something and check whether termination condition is met
combination = binaryCombinations.next().value;
}
}
In my unit tests (using Jasmine) I want to verify how many times the generator function is invoked (i.e. how many combinations are generated) before the termination. Below is what I have tried:
it("My spec", () => {
//arrange
const generatorSpy = spyOn(Utility, "binaryCombinationGenerator").and.callThrough();
//act
//assert
expect(generatorSpy).toHaveBeenCalledTimes(16); // fails with: Expected spy binaryCombinationGenerator to have been called 16 times. It was called 1 times.
});
However, this assertion fails as binaryCombinationGenerator is indeed called once. What I actually want to spy on is the next method of IterableIterator.
However, I am not sure how to do that. Please suggest.
You could return a jasmine spy object from the Utility.binaryCombinationGenerator method
let binaryCombinationsSpy = jasmine.createSpyObject('binaryCombinations', ['next']);
binaryCombinationsSpy.next.and.returnValues(value1, value2);
spyOn(Utility, "binaryCombinationGenerator").and.returnValue(binaryCombinationsSpy);
expect(binaryCombinationsSpy.next).toHaveBeenCalledTimes(2);
I am posting this as an answer as this is what I have done to mock the generator function. This is based on #0mpurdy's answer.
To mock the generator function, we actually need to call a fake function, which will provide different (and limited, if applicable) values for each call of next() of the generator function.
This can be simply achieved by the following:
//arrange
const streamSpy= jasmine.createSpyObj("myGenerator", ["next", "counter"]);
streamSpy.counter = 0;
const values = [{ value: here }, { value: goes }, { value: your }, { value: limited },
{ value: values }]; // devise something else if it is an infinite stream
// call fake function each time for next
streamSpy.next.and.callFake(() => {
if (streamSpy.counter < values.length) {
streamSpy.counter++;
return values[streamSpy.counter - 1];
}
return { value: undefined }; // EOS
});
spyOn(Utility, "myGenerator").and.returnValue(streamSpy);
...
//assert
expect(streamSpy.next).toHaveBeenCalledTimes(2);

Categories