简体   繁体   中英

Jasmine how pass specs that run in any order that depend on a closure variable

I feel like this should be really intuitive... but maybe it doesn't work the same way I'm used to thinking of it in Rspec... Here's the function I'm trying to test:

var finalSet = (function() {
  var mount = false

  return {
    initializePage: function() {
      if (mount == false) {
        this.mountForm();
        mount = true;
      }
      this.greetCustomer();
    },
    greetCustomer: function() {
      ...
    },
    mountForm: function() {
      ...
    }
  }
})();

The gist is that mount is a closure variable that ensures that regardless how many times initializePage is called, mountForm is only called once.

This is my current spec:

describe("finalSet", function() {
  describe("initializePage", function() {
    beforeEach(function() {
      spyOn(finalSet, "mountForm")
      spyOn(finalSet, "greetCustomer")
    })
    describe("first initialization", function() {
      beforeEach(function() {
        finalSet.initializePage();
      })
      it("calls both methods", function() {
        expect(finalSet.mountForm).toHaveBeenCalled()
        expect(finalSet.greetCustomer).toHaveBeenCalled()
      })
      describe("initialize again", function() {
        beforeEach(function() {
          finalSet.initializePage();
        })
        it("repeat calls only greetCustomer", function() {
          expect(finalSet.mountForm.calls.count()).toEqual(1)
          expect(finalSet.greetCustomer.calls.count()).toEqual(2)
        })
      })
    })
  })
})

If I run the specs individually, they pass. Together, and at random, only 1 will pass, the first spec that is run, the second will always fail.

I know why this is happening... but not the appropriate Jasmine set up to fix this issue.

The reason it's happening is because finalSet mounts once across all specs, meaning the closure is set once despite there being two specs. So what happens is that if the "first initialization" test runs first, the beforeEach call to finalSet.initializePage() sets the mount = true , this first spec passes, but then, come the "initialize again" test, mount is still true, it doesn't reset, so neither of the 2 beforeEach calls to finalSet.initializePage() will ever call mountForm`, since that happened in the first spec that passed. Hence the second spec fails.

On the other hand, if "initialize again" runs first, the stacked 2 beforeEach calls to finalSet.initializePage() work as expected, mountForm is called once, greetCustomer is called twice. But then when the first "first initialization" spec is called, mount = true already, so nothing gets called.

The immediate question: is there a way to reset the closure as part of the first beforeEach ?

The larger question: I'm used to this kind of nested speccing from Rspec, but... I'm feeling like maybe this is NOT how one should test in Jasmine, so very open to what a better way of testing this would be is.

Thanks!

You're trying to test what's essentially a global object across multiple specs. You'll modify your code in some way to test what you want. Here are some ideas:

  1. Add a reset() function to the exported object that sets the mount variable to false, Call this function in a before/afterEach in your specs. This is similar to how you might reset a database (a global resource) in a Rails spec before every test.

  2. Have your code return a function instead of immediately invoking it. You might rename the finalSet variable as createFinalSet. This means you have a finalSet factory instead of a global instance or singleton; each test would have its own finalSet via finalSet = createFinalSet() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM