I'm using a closure to ensure that something is only called once:
var pageDOM = (function() {
var mounted = false
return {
initializePage: function() {
if (mounted == false) {
pageDOM.addBoxes();
mount = true
}
pageDOM.otherInitProcedures();
},
otherFunction: function() {
}
}
})();
I'm not sure what's the right way of thinking about unit testing pageDOM.initializePage. Jasmine specs are run in random order, and I think it's important to keep this for testing integrity (i.e., I would NOT want to impose order). This is my spec code:
describe("pageDOM", function() {
describe("initializePage", function() {
beforeEach(function() {
spyOn(pageDOM, "addBoxes")
spyOn(pageDOM, "otherInitProcedures")
})
describe("calling initializePage first time", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should call both functions", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).toHaveBeenCalled()
})
describe("calling initializePage again", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should only call otherInitProcedures", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).not.toHaveBeenCalled()
})
})
})
})
})
The problem is that if the specs don't run in order, then both will fail. What's a way to test this, or should I even try to test this?
I would assign the spies to variables and reset the spies in an afterEach hook.
Something like this (follow the !! in the comments):
describe("pageDOM", function() {
describe("initializePage", function() {
// !! initialize these variables
let addBoxesSpy;
let otherInitProceduresSpy;
beforeEach(function() {
// !! assign the variables
addBoxesSpy = spyOn(pageDOM, "addBoxes")
otherInitProceduresSpy = spyOn(pageDOM, "otherInitProcedures")
})
describe("calling initializePage first time", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should call both functions", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).toHaveBeenCalled()
})
describe("calling initializePage again", function() {
beforeEach(function() {
pageDOM.initializePage();
})
it("should only call otherInitProcedures", function() {
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).not.toHaveBeenCalled()
})
})
})
// !! Reset the spies in an afterEach
afterEach(() => {
addBoxesSpy.calls.reset();
otherInitProceduresSpy.calls.reset();
});
})
})
After resetting the calls to what you're spying on, order should not matter anymore.
So your "pageDOM" method is state full, so why use 2 times describe and set the call to "initializePage" method every time by hooking it in beforeEach, doesn't make sense. Instead you can do like this -
describe("pageDOM:initializePage", function() {
describe("calling initializePage first time", function() {
beforeEach(function() {
spyOn(pageDOM, "addBoxes");
spyOn(pageDOM, "otherInitProcedures");
})
it("should call both functions", function() {
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).toHaveBeenCalled()
})
it("should only call otherInitProcedures", function() {
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalled()
expect(pageDOM.addBoxes).not.toHaveBeenCalled()
})
})
})
Jasmine executes it blocks within a describe sequentially and you can get the desired checks as well. Working stackblitz link for you(ignore other testcases)
To make the code more testable, I would not initiate the function immediately.
var pageDOMConstructor = vfunction() {
var mounted = false
return {
initializePage: function() {
if (mounted == false) {
pageDOM.addBoxes();
mount = true
}
pageDOM.otherInitProcedures();
},
otherFunction: function() {
}
}
};
var pageDOM = pageDOMConstructor();
then you can test pageDOMConstructor easily.
To check how often something has been called you can use toHaveBeenCalledTimes
this is not quite complete and might need some small changes, its just to give you an idea of how to solve this:
describe("pageDOMConstructor", function () {
describe("initializePage", function () {
// setup variable, its a let because it will be reset before every test
let pageDom;
beforeEach(function () {
pageDom = pageDOMConstructor();
spyOn(pageDOM, "addBoxes");
spyOn(pageDOM, "otherInitProcedures");
});
it("should call both functions when calling initializePage first time", function () {
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalledTimes(1);
expect(pageDOM.addBoxes).toHaveBeenCalledTimes(1);
});
it("should only call otherInitProcedures when calling initializePage again", function () {
pageDOM.initializePage();
// you could remove these two lines because they are in the other test
expect(pageDOM.otherInitProcedures).toHaveBeenCalledTimes(1);
expect(pageDOM.addBoxes).toHaveBeenCalledTimes(1);
pageDOM.initializePage();
expect(pageDOM.otherInitProcedures).toHaveBeenCalledTimes(1);
expect(pageDOM.addBoxes).not.toHaveBeenCalledTimes(1);
});
});
});
Each it should be treated like a separate test, and they should be completely self reliant - with some exceptions like beforeEach
When code is hard to test, it's often a sign that you could benefit from refactoring it a bit. I have found that testable code equals usable and flexible code in production.