Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

250
Views
Jasmine - how to test functions that should only be called once (executed via a closure variable)

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?

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

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.

about 4 years ago · Juan Pablo Isaza Report

0

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)

about 4 years ago · Juan Pablo Isaza Report

0

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.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!