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

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.

Related

How to test js factory function using mocha & chai

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.

Jasmine Spy to return different values based on argument

I am spying a JS method. I want to return different things based on actual argument to the method. I tried callFake and tried to access arguments using arguments[0] but it says arguments[0] is undefined.
Here is the code -
spyOn(testService, 'testParam').and.callFake(function() {
var rValue = {};
if(arguments[0].indexOf("foo") !== -1){
return rValue;
}
else{
return {1};
}
})
This is suggested here - Any way to modify Jasmine spies based on arguments?
But it does not work for me.
Use of arguments should work just fine. Also on a side note could you paste your entire object under test- though that's not the source of the issue.
Here is how I used it. See it in action here
var testObj = {
'sample': "This is a sample string",
'methodUnderTest': function(param) {
console.log(param);
return param;
}
};
testObj.methodUnderTest("You'll notice this string on console");
describe('dummy Test Suite', function() {
it('test param passed in', function() {
spyOn(testObj, 'methodUnderTest').and.callFake(function() {
var param = arguments[0];
if (param === 5) {
return "five";
}
return param;
});
var val = testObj.methodUnderTest(5);
expect(val).toEqual('five');
var message = "This string is not printed on console";
val = testObj.methodUnderTest(message);
expect(val).toEqual(message);
});
});

Passing Object's Instance in Javascript

Here is what I'm trying to do. I'm trying to pass an instance of order to bill, where it would be indexed. The thing is that it's not working.
Am I stretching JS too thin here?
Any example on how to do this, or some reading material?
EDIT: Maybe I should add that this is supposed to be the user interface for a POS (Point of Sale) system. It should accept the order of products (each one with variable quantity), and process in the client's side the subtotal, total and number of items in the bill.
EDIT2: Not native english speaker. Maybe the names that I choose did not best suited the problem.
function Bill (prefix,maxForms,minForms) {
this.prefix = prefix;
this.maxForms = maxForms;
this.minForms = minForms;
this.items = [];
this.total = 0;
this.addOrder = function(order) {
if (this.items.length == 0)
{
this.items.push(order);
}
for (i=0;i<this.items.length;i++){
if (this.items[i].name === order.name) {
this.items[i].quantity = order.quantity;
this.items[i].price = order.price;
}
else {
this.items.push(order);
}
this.total = this.total + order.getSubTotal();
}
}
}
function Order (name,price,quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
this.getSubtotal = function () {
return this.price*this.quantity;
}
this.changeQuantity = function (newQuantity) {
this.quantity = newQuantity;
}
this.incrementQuantity = function () {
this.quantity = this.quantity + 1;
}
}
Here's an issue:
for (i = 0;/*...*/)
I would suggest you spend a little more time in JS.
It does look a lot like C / Java / C# / PHP, etc...
The problem, however, is that JS does not have any notion of block scope*.
* until ES6, that is
It only deals with function scope.
That is, a variable has the same reference through the whole function where it's defined (via var).
If a variable is not defined via var, the function goes up to its parent to find the value of the variable, and up from there, and up from there, until it hits window.<varname>.
You might actually be modifying window.i in your class' instance.
function Bill ( ) {
var bill = this,
i = 0;
for (i=0; /* ... */) { /*...*/ }
}
That said, you might do to spend time getting to know JS.
Most of what you've written looks absolutely fine, in English, as well.
I might break it down a little further:
function Bill () {
var bill = this;
extend(bill, {
total : 0,
items : [],
addOrder : function (order) {
var match = bill.findOrder(order.name);
if (!match) { bill.items.push(order); }
else { bill.updateOrder(match, order); }
bill.updateTotal();
},
findOrder : function (name) {
var matches = bill.items.filter(function (order) {
return order.name === name;
});
return matches[0];
},
updateOrder : function (current, updated) {
/* I don't know if you want to replace the old order, or add to it... */
/* so I'm "replacing" it, instead of increasing quantity, like you did */
current.quantity = updated.quantity;
current.price = updated.price;
},
updateTotal : function () {
bill.total = bill.items
.map(function (order) { return order.getSubtotal(); })
.reduce(function (tally, price) { return tally + price; }, 0);
}
});
}
var bill = new Bill();
bill.addOrder(new Order(/*...*/));
I'm doing a few things differently, here.
First, extend isn't a "built-in" function; there are a lot of implementations, in all sorts of libraries, but basically, it just saves me from writing bill.x = x; bill.y = y; bill.z = z;..., and use an object, instead.
Next, I'm using var bill = this;
and bill.method = function () { bill.total = /*...*/; };
instead of this.method = function () { };, because once you go two levels down, in functions, this no longer means the object you think it does.
this.method = function () {
this.async(function (response) {
// unless you change it yourself, `this` probably means `window`
this.value = response; // oops
});
};
// instead, try
var thing = this;
thing.method = function () {
thing.async(function (response) {
thing.value = response;
});
};
Of course, you can always mix and match, as long as you know how far down you can go (one level)...
...but that means you really, really need to care about using this a whole lot.
var thing = this;
this.method = function () {
this.async(function (val) {
thing.value = val;
});
};
Much more confusing than just referring to the instance by a variable, rather than combining the two.
There are dozens of ways of doing this; some look very class-like, others might be 100% functional, and in ES6, you might just use classes altogether.
But there are some ideas, and some reasons behind doing them that way (especially if you don't know where the differences are in JS vs the other C-looking languages).
I don't think you're stretching JS too thin, at all.
Once all of the issues on line 80 are fixed. All you need to do is:
var order = new Order("My Order", 12, 2);
var bill = new Bill(blah, blah, blah);
bill.addOrder(order);
A few issues right off the bat:
this.total = this.total + order.subTotal();ยท
There is a garbage char at the end.
Order does not have a subtotal function. It should be getSubtotal.
The 2 assignments to this.items[i].quantity and this.items[i].price are superfluous, since you are assigning properties to themselves. Remember, this.items[i] === order. This is not a bug, but it is inefficient.
You should have something like this.total = 0; at the top of Bill.
I think you want:
this.items[i].quantity += order.quantity;
this.items[i].price += order.price;
This will update quantity with whatever quantity order has. Secondly, I see you have an order function. Not an order object. Was that intentional? Are you planning to add instances of this bill/order object to each other? I don't think that's where you were going. Make sure they are separate objects that you are nesting.
Are you getting anything except undefined? I don't think you are because you're not returning anything.
Put:
return this;
at the end of your functions. Make sure you save them to a var when you make them:
bill = Bill(v,v,v);
order = Order(v,v,v);
then you can:
bill.addOrder(order);
See if that helps.

Needing some visitor-like design pattern

I will give you a sample example of my problem to remove the logical complexity and let you be focus on the important part. Of course, this example will be a bit useless...
I have a tree structure where node are like that
{
path: "...",
childs : []
}
Now, I have to write all the full paths from root to each leaf in an array.
My design is very poor:
function listPaths(node) {
var result = [];
function listForNode(n, parentFullPath) {
var thisPath = parentFullPath + "/" + n.path;
result.push(thisPath);
n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
listForNode(node, "");
return result;
}
It could be nice but I can't write the test with Mocha without having an insane 600 line code test file. At this moment, you should be asking why. The reason is the complexity of the real purpose, that's not relevant for my question. My goal is to having something 'mockable' cause I'm used to. (Java dev). But I fail.
Do you have any pattern that I can use to resolve this one? I'm not really good at JS patterns. :/
Visitor? Making an Y Combinator? So many possibility...
Thank you for reading me
You need to remember that functions are first class citizens in javascript.
I see that essentially what you have is something like
function createVisitor(parentsAccumulatorInitialValue, parentsAccumulator){
var visitor = function myVisitor (node) {
var result;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
result.push(thisPath);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
result = [];
listForNode(node, parentsAccumulatorInitialValue());
return result;
}
return visitor;
}
var listPaths = createVisitor(
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
But that's not the only abstraction you could take care of:
function createVisitor2(
totalAccumulatorInitialValue,
totalAccumulator,
parentsAccumulatorInitialValue,
parentsAccumulator){
var visitor = function myVisitor (node) {
var total;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
total = totalAccumulator(total, thisPath, n);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
total = totalAccumulatorInitialValue();
listForNode(node, parentsAccumulatorInitialValue());
return total;
}
return visitor;
}
var listPaths2 = createVisitor2(
function totalInit() {
return [];
},
function totalAcc(total, thisPath, n){
total.push(thisPath);
return total;
},
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
Which might be pretty reasonable, but as you can see, I'm already beginning to have trouble finding appropriate names for these variables. In fact, I'd say the name of our function is bad, as doesn't create anything strictly like a visitor object I know of. However, it does work (BTW, I've slightly modified it to handle nulls as well as empty arrays):
> listPaths( { path:"foo",
childs: [{path:"bar", childs: null}, {path:"bob", childs: null}]})
["/foo", "/foo/bar", "/foo/bob"]
It can be modified even further so that your trees don't strictly even have the same structure... but we're already at 4 parameters, which isn't great. It'd be better if your visitor creator were passed a single extensible object with all the necessary methods or values. For instance, maybe (pseudocode):
function createVisitor3(opts) {
//assume we've defined GetDefaults() somewhere local to createVisitor3
// as well as assume that extend is defined somewhere that copies properties
// into a new object like various previously existing libraries do.
opts = extend({}, GetDefaults(), opts);
var totalAccumulatorInitialValue = opts.totalAccumulatorInitialValue;
var totalAccumulator = opts.totalAccumulator;
var parentsAccumulatorInitialValue = opts.parentsAccumulatorInitialValue;
var parentsAccumulator = opts.parentsAccumulator;
var childrenGetter = opts.childrenGetter;
/// etc.
...
}

Have a javascript function privately track it's number of calls

I'm trying to figure out how I can have a javascript function privately track the number of times it has been called. The objective is to be able to query this value in the console during debugging by doing func.run
My first attempt:
function asdf() {
if (!asdf.run) {
asdf.run = 0;
} else {
asdf.run++;
console.error('run: ' + asdf.run);
}
console.error('asdf.run def: ');
console.error(asdf.run);
}
asdf();
This is a good lesson of why one should ALWAYS aim to use === in nearly all javascript booleans, cause they could secretly be ==
Closures are the way to go here:
var asdf = (function () {
var runs = 0;
var f = function () {
++runs;
// your function here
};
f.runs = function () {
return runs;
};
return f;
}());
Usage:
asdf();
asdf();
asdf.runs(); // 2
asdf();
asdf.runs(); // 3
Or, you could use a mocking framework like (shameless self plug) Myrtle.
Your first try would work fine except you've forgotten that 0 is a "falsy" value in JavaScript, so on the first run and every run thereafter !this.run will evaluate to true and your else block will never be reached. This is pretty easy to work around.
function foo() {
if(typeof(foo.count) == 'undefined') {
foo.count = 0;
} else {
foo.count++;
}
console.log(foo.count);
}
foo(); # => 0
foo(); # => 1
foo(); # => 2
# ...
I haven't actually tried this, but I looked up "static function variables in JavaScript", and I found this resource. I think the main difference between what you wrote and what's in that solution is how the first run of the function is detected. Perhaps your !asdf.run test is not working the way you thought it was, and you should use typeof asdf.run == 'undefined' to test instead.
OK, here is a method that I came up with that does not require the function to be modified at all.
So if you have this.
function someFunction() {
doingThings();
}
you could add a counter like this...
addCounter(this, "someFunction");
where this is the scope you are in, you could use any object that has a method you want to count.
Here's the code for it.
<html>
<head>
<script>
function someFunc() {
console.log("I've been called!");
};
// pass an object, this or window and a function name
function wrapFunction(parent, functionName) {
var count = 0, orig = parent[functionName];
parent[functionName] = function() {
count++;
return orig.call(this, Array.prototype.slice(arguments));
}
parent[functionName].getCount = function() {
return count;
};
}
var someObj = {
someFunc: function() {
console.log("this is someObj.someFunc()");
}
}
wrapFunction(this, "someFunc");
wrapFunction(someObj, "someFunc");
someFunc();
someObj.someFunc();
someObj.someFunc();
someObj.someFunc();
console.log("Global someFunc called " + someFunc.getCount() + " time" + (someFunc.getCount() > 1 ? "s" : "")) ;
console.log("Global someObj.someFunc called " + someObj.someFunc.getCount() + " time" + (someObj.someFunc.getCount() > 1 ? "s" : "")) ;
</script>
</head>
So, !asdf.run is a form of the double equals operator == and I had set asdf.run to 0 so it was false.
Using the triple equals === :
typeof asdf.run === "undefined" for the boolean solves my issue.
So a final usable and useful version:
function sdf() {
if (typeof sdf.run === "undefined") { sdf.run = 0; }
sdf.run++;
}
To query the number of times sdf has been called:
sdf.run;
To actually make this variable private and protect it from change, one would implement a closure.
//using a closure and keeping your functions out of the global scope
var myApp = (function() {
//counter is a private member of this scope
var retObj = {}, counter = 0;
//function fn() has privileged access to counter
retObj.fn = function() {
counter++;
console.log(counter);
};
//set retObj to myApp variable
return retObj;
}());
myApp.fn(); //count = 1
myApp.fn(); //count = 2
myApp.fn(); //count = 3
You don't necessarily need a closure. Just use a static variable.
var foo = function(){
alert( ++foo.count || (foo.count = 1) );
}
// test
function callTwice(f){ f(); f(); }
callTwice(foo) // will alert 1 then 2
or
callTwice( function bar(){
alert( ++bar.count || (bar.count = 1) );
}); // will alert 1 then 2
the second one is a named anonymous function. And note this syntax:
var foo = function bar(){ /* foo === bar in here */ }

Categories

Resources