How to test js factory function using mocha & chai - javascript

I am trying to test my DOM project, so it should make sure that the cost is 2.75 and sms is 0.75. It returns an assertion error that says expected 2.75 to equal undefined. I need help
accessing the correct values of call and sms.
Here's my factory function
var callCost = 0;
var smsCost = 0;
var totalCost = 0;
const warning = 30;
const critical = 50;
function getCall() {
return callCost;
}
function getSms() {
return smsCost;
}
function getTotal() {
totalCost = callCost + smsCost;
return totalCost;
}
function radioButtons(selectedBill) {
if (selectedBill === "call") {
callCost += 2.75;
} else if (selectedBill === "sms") {
smsCost += 0.75;
}
}
function totalClassName() {
if (getTotal() >= warning && getTotal() < critical) {
return "warning";
} else if (getTotal() >= critical) {
return "critical";
}
}
return {
getCall,
getSms,
getTotal,
radioButtons,
totalClassName
}
}
describe('The radio-bill function', function(){
it('Should be able to add call at 2.75', function(){
var itemType = RadioBill();
itemType.radioButtons("call");
assert.equal(2.75, itemType.radioButtons("call"))
})
})

You only need to change your assert line to get your test working.
var itemType = RadioBill();
itemType.radioButtons("call");
assert.equal(itemType.getCall(), 2.75);
Here, the first thing to note is that the order of the arguments in a call to assert does matter. The first argument is the actual value, the second one is the expected value. Typically, but not always the actual value will be the result of an operation, and the expected value will be constant.
The second point is that in your code the function radioButtons does not return a value, it just changes the value of an internal state variable. But there is already the function getCall to get that value, and that is what the assert line is checking.

Related

Range Error: Maximum call stack size exceeded - JavaScript

I'm creating a function saveOutput that accepts a function, and a string. saveOutput will then return a function that behaves exactly like the passed-in function, except for when the password string is passed in as an argument. When this happens, the returned function will return an object with all previously passed-in arguments as keys, and the corresponding outputs as values.
I think my code below is correct but I run into the Range Error: Maxiumum call stack size exceeded when I run my code.
function saveOutput(inputFunc, string) {
let obj = {};
//inputFunc() accepts one args
//string like a pwd
return function inputFunc(input) {
if (input === string) {
return obj;
} else {
obj[input] = inputFunc(input);
return inputFunc(input);
}
}
//returns a fxn
return inputFunc;
}
//Test cases
const multiplyBy2 = function(num) { return num * 2; };
const multBy2AndLog = saveOutput(multiplyBy2, 'boo');
console.log(multBy2AndLog(2)); // should log: 4
console.log(multBy2AndLog(9)); // should log: 18
console.log(multBy2AndLog('boo')); // should log: { 2: 4, 9: 18 }
You're using the name inputFunc twice. The returned function is called inputFunc so it shadows the callback function passed as parameter. The returned function calls inputFunc which is itself and causes an endless recursion and eventually the "maxiumum call stack size exceeded" error is thrown.
To fix this either use a different name or make it anonymous as the name is not needed anyway, here is the working code with some improvements:
function saveOutput(inputFunc, string) {
let obj = {};
return function (input) { // make it anonymous
if (input === string) {
return obj;
}
// improvement 1: the else block can be omitted here
return obj[input] = inputFunc(input); // improvement 2: no need to call inputFunc twice, just assign and return at the same time
}
// the return statement here is never reached because there is a return right before it so just remove it
}
Read more about variable shadowing here: An example of variable shadowing in javascript
Demo:
function saveOutput(inputFunc, string) {
let obj = {};
return function(input) {
if (input === string) {
return obj;
}
return obj[input] = inputFunc(input);
}
}
const multiplyBy2 = function(num) {
return num * 2;
};
const multBy2AndLog = saveOutput(multiplyBy2, 'boo');
console.log(multBy2AndLog(2));
console.log(multBy2AndLog(9));
console.log(multBy2AndLog('boo'));

How to get Functions output and re-use within IF/else Statement

I have function at the bottom of my script which counts for how many tools were used.
Then based on how many tools were used I want to perform different actions.
I can easily check the output of the Function but I struggle to put it into If statement.
How to get Functions output and re-use it within IF Statement?
var HowManyTools = HowManyTools1();
if (HowManyTools <= 2) {
Category13();
} else if (HowManyTools >= 6) {
Category14();
} else if (HowManyTools > 2) {
Category12();
}
function HowManyTools1() {
//returns value between 1-9
}
Update: I've added if to the last else. It executes the Category13();. Without if all previous statements were simply false so it went straight to the last statement with Category12();
I can output into Category13/ Category12. But not the Category14.
It seems like my function can't get defined, as soon as I put it within a variable, and if I try to alert(HowManyTools) I simply get undefined Error.
Tried a few examples from here but to no avail
This variant should demonstrate that every one of your Category functions will run if its if-condition is met.
I pass HowManyTools to each of those functions for demonstration purposes.
for (var i = 0; i < 20; i++) {
test();
}
function test () {
var HowManyTools = HowManyTools1();
if (HowManyTools <= 2) {
Category13(HowManyTools);
} else if (HowManyTools >= 6) {
Category14(HowManyTools);
} else if (HowManyTools > 2) {
Category12(HowManyTools);
}
}
function HowManyTools1() {
return Math.floor(Math.random() * 9 + 1);
}
function Category12(val) {
console.log(`Category 12: value = ${val}`);
}
function Category13(val) {
console.log(`Category 13: value = ${val}`);
}
function Category14(val) {
console.log(`Category 14: value = ${val}`);
}

how can i return class method result only after last call

`enter code here`
class SmartCalculator {
constructor(initialValue) {
this.priority = 0;
this.decision = initialValue;
}
add(number) {
this.priority = 2
this.decision = this.decision + number
//return this.decision
}
subtract(number) {
this.priority = 2
}
multiply(number) {
this.priority = 1
}
devide(number) {
this.priority = 1
}
pow(number) {
this.priority = 0
}
}
module.exports = SmartCalculator;
I have js class, and some test thats looks like:
const calculator = new SmartCalculator(1);
const value = calculator
.add(5)
.add(5);
assert.equal(value, 11);
});
If I return the result immediately after calling the add method, I get an error.
How can I return the result only after the last method ?
If you want to chain calls to add, multiply etc, they should all return this. The line calculator.add(5).add(5) takes calculator, calls the leftmost add(5) onto it, gets the result of that (which is 6), and tries to call add(5) on it. 6.add(5) raises an error.
If you returned this, the leftmost call would return the updated calculator, onto which add(5) can be called again.
Then you need to implement a method like getResult() or something which returns this->decision, and add a call to this method after your adds: calculator.add(5).add(5).getResult().
How about changing it slightly to:
const { value } = calculator
.add(5)
.add(5);
Then you just have to
return this;
at the end of every method and add a small getter to your class:
get value() { return this.decision; }

How can I stub a problematic piece of code [Unit Testing]

New to writing unit tests and the concept of 'mocking' to be more exact. I have a basic function 'addPercentSign' that adds a percent character to a user input if it is between 50-100:
addPercentSign: function (oEvent, control) {
var inputVal = oEvent.getParameters().value;
var inputNumber = parseFloat(inputVal);
if (inputNumber) {
if (inputNumber < 50 || inputNumber > 100) {
return null;
} else {
var finalVal = inputNumber.toFixed(1);
var finalOutput = finalVal + "%";
//Error: cannot setValue of undefined. How can I 'stub' the line below?
control.learningCurve.setValue(finalOutput);
return finalOutput;
};
}
}
The Problem
The problem I'm facing is when I write a unit test for this function, I cannot test the returned value(finalOutput) because the line above it returns an error as it relies on the DOM element (control.learningCurve) to set a value. Since this is a unit test, I must isolate this function from any dependencies.
Therefore, I need to 'mock' the 'setValue' call. I figured creating a stub would make sense, but I'm not sure how?
Here is the unit test code that I'd like to test the above function (addPercentSign):
function (formatter, viewControls) {
"use strict";
var testEvent = {
getParameters : function() {
return {value : 50}
}
}
QUnit.module("Formatter Functions");
QUnit.test("Add Percent Sign", function (assert) {
assert.ok(formatter.addPercentSign(testEvent, viewControls) == '50.0%', "Percent Sign Added: Pass");
});
}
The Question
How can I mock the setter so I can unit test this function without DOM dependencies?:
control.learningCurve.setValue(finalOutput)
Sinon can be used as the test double.
This example is using sinon stub.
sinon.stub(control.learningCurve, 'setValue').returns('value that you need');
Edit:
function (formatter, viewControls) {
"use strict";
sinon.stub(control.learningCurve, 'setValue').returns('value that you need');
var testEvent = {
getParameters : function() {
return {value : 50}
}
}
QUnit.module("Formatter Functions");
QUnit.test("Add Percent Sign", function (assert) {
assert.ok(formatter.addPercentSign(testEvent, viewControls) == '50.0%', "Percent Sign Added: Pass");
});
}
I'm not sure as I am not a JS Ninja but I think it shouldn't be so hard.
Method you want to use is inside an object with name 'learningCurve' that's inside 'control' object.
If control is inside your formatter, can't you just do this:
formatter.control = {};
formatter.control.learningCurve = {};
formatter.control.learningCurve.setValue = function() {
return something_to_return;
}
assert.ok(formatter.addPercentSign(testEvent, viewControls) == '50.0%', "Percent Sign Added: Pass");
If it's not inside formatter than do that by creating a new instance of control and replacing what's inside like above.
I find one of the main added value of unit testing is that it makes you realize where your code could be refactored. In your case, there are 2 distinct concerns: Appending the % and modifying the DOM. By refactoring those 2 concerns into their individual functions, you can unit test your logic without mocking anything.
//Formerly addPercentSign
inputChanged: function (oEvent, control) {
var inputVal = oEvent.getParameters().value;
var inputNumber = parseFloat(inputVal);
var formattedNumber = addPercentSign(inputNumber);
control.learningCurve.setValue(formattedNumber);
}
function addPercentSign(inputNumber) {
if (inputNumber < 50 || inputNumber > 100) {
return inputNumber;
}
var finalVal = inputNumber.toFixed(1);
return finalVal + "%";
}
Now you can test addPercentSign easily. You could unit test inputChanged as well, but that would just be unit testing the javascript framework.

Calling a function without entering its parameters in Javascript

I am trying to create a JavaScript version of an old timey dice game named PIG. It involves rolling dice and keeping track of the sum of each roll. I need to be able to save each roll within a function and then be able to call said function to return the current roll score.
This is the method that takes the amount of the current roll and stores it into the variable total, subsequently returning the current total variable.
function rollTotal(amount) {
return amount;
}
I first call the function and insert the score of the dice roll into the amount parameter...
var dieOne = 1;
var dieTwo = 2;
rollTotal(dieOne + dieTwo);
Then I call the function to return the current score like this...
rollTotal()
and am getting this...
undefined
Is there a way to set a default parameter to return the current score if a parameter is not entered when called?
When you call a function and don't supply a parameter, then the parameter is bound to undefined within the function. So you can do something like this:
var currentAmount = undefined; // or perhaps 0?
function rollTotal(amount) {
if (typeof(amount) === 'undefined') {
return currentAmount;
} else {
currentAmount = amount;
return amount;
}
}
You can do this more elegantly (and more safely) using a closure:
var rollTotal = (function() {
var currentAmount = undefined;
return function(amount) {
if (typeof(amount) === 'undefined') {
return currentAmount;
} else {
currentAmount = amount;
return amount;
}
};
}());
You might also want to do a more robust test of the argument and treat any non-number as undefined. You can do this with the following test:
!isNaN(parseFloat(amount)) && isFinite(amount)
instead of testing specifically for undefined.
EDIT: As #Paul S. points out, the best test as to whether an argument was passed is arguments.length === 0.
Why use a function? The = operator works just fine.
// initial setup
var rollTotal;
// set in the middle of an expression
"You just rolled " + (rollTotal = dieOne + dieTwo) + "!";
// recalling later in an expression
"Last time you rolled " + rollTotal + ", what will you get this time?";
A popular way to do this is with an instance of a class.
That is:
function Dice() {
var amount;
this.rollTotal = function(_amount) {
// if an argument is supplied, assign it to the internal amount
if (typeof _amount !== "number") {
amount = _amount;
}
return amount;
};
}
Example usage:
var dice = new Dice();
dice.rollTotal(3);
console.log(dice.rollTotal());
In JavaScript you can look at the arguments object to check whether an argument was passed or not when the function was called. Here is how you can check if any argumemt was passed upon calling the function.
function rollTotal(amount){
if(arguments.length === 0)
return (a value when no arguments were passed);
else return amount;
}

Categories

Resources