Most JavaScript developers are familiar with the concepts of truthy
and falsy
. As a quick refresher, the falsy
values are:
false
null
undefined
0 (the number zero)
"" (an empty string)
Everything else is truthy
. This is one of those things you just have to memorize, but it's simpler if you think of the falsy
stuff as things that are false or have no value. Just watch out for zero and empty string.
I got burned by a bug today in a passing Jasmine test by not paying enough attention to the truthy
values. The code I was testing was a little function to make sure scaleMin
was smaller than scaleMax
, but if they were both set to 0, that was OK. If scaleMin
was greater than or equal to scaleMax
, we'd return an error message.
Here are the function and tests I started with. All the tests pass. Done, right?!?!
// Code under test
var MyApp = function () {
var isValid = function (scaleMin, scaleMax) {
if (scaleMax <= scaleMin) {
return "Scale max must be greater than Scale min.";
}
return true;
};
return {
isValid: isValid
};
};
// Specs
describe("isValid", function () {
var myApp = new MyApp();
it("Should be valid when scale min/max are both 0", function () {
var scaleMin = 0,
scaleMax = 0;
expect(myApp.isValid(scaleMin, scaleMax)).toBeTruthy();
});
it("Should be valid when scale min is less than scaleMax", function () {
var scaleMin = 0,
scaleMax = 1;
expect(myApp.isValid(scaleMin, scaleMax)).toBeTruthy();
});
it("Should be invalid when scale min is equal to scaleMax", function () {
var scaleMin = 1,
scaleMax = 1;
expect(myApp.isValid(scaleMin, scaleMax)).toBe("Scale max must be greater than Scale min.");
});
it("Should be invalid when scale min is greater than scaleMax", function () {
var scaleMin = 1,
scaleMax = 0;
expect(myApp.isValid(scaleMin, scaleMax)).toBe("Scale max must be greater than Scale min.");
});
});
Not so much.
Jasmine's toBeTruthy()
checks for any truthy
value, and a string with something in it is truthy
. So in the test where I'm checking that it's OK if scaleMin
and scaleMax
are both 0, I'm getting the error string, which is truthy
. Since the error string and the value true are both truthy
, my check for toBeTruthy()
will always pass. I'm getting false positives.
This is why you are supposed to write a failing test, then a passing test to prove your code was the thing that made the test pass. I skipped that here because this code was so simple. What could go wrong? Oops!
Here's the fixed function and test code, which uses Jasmine's toBe(true)
instead of toBeTruthy()
to tease apart error strings and the value true:
// Code under test
var MyApp = function () {
var isValid = function (scaleMin, scaleMax) {
if (scaleMin === 0 && scaleMax === 0) {
return true;
}
if (scaleMax <= scaleMin) {
return "Scale max must be greater than Scale min.";
}
return true;
};
return {
isValid: isValid
};
};
// Specs
describe("isValid", function () {
var myApp = new MyApp();
it("Should be valid when scale min/max are both 0", function () {
var scaleMin = 0,
scaleMax = 0;
expect(myApp.isValid(scaleMin, scaleMax)).toBe(true);
});
it("Should be valid when scale min is less than scaleMax", function () {
var scaleMin = 0,
scaleMax = 1;
expect(myApp.isValid(scaleMin, scaleMax)).toBe(true);
});
it("Should be invalid when scale min is equal to scaleMax", function () {
var scaleMin = 1,
scaleMax = 1;
expect(myApp.isValid(scaleMin, scaleMax)).toBe("Scale max must be greater than Scale min.");
});
it("Should be invalid when scale min is greater than scaleMax", function () {
var scaleMin = 1,
scaleMax = 0;
expect(myApp.isValid(scaleMin, scaleMax)).toBe("Scale max must be greater than Scale min.");
});
});