Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Why in Jasmine, we cannot put the expect in an outside function? #348

Closed
jianlin opened this Issue · 5 comments

3 participants

@jianlin

http://stackoverflow.com/questions/15893168/why-in-jasmine-we-cannot-put-the-expect-in-an-outside-function

If using Jasmine 1.3.1, I use

describe("TryTry", function() {

    var i;

    function checkForSituation(a) {
        // say, if this is made into a function because 
        //   there are a lot of processing

        console.log("THERE", a); 
        expect(foo(3, a)).toEqual( 3 + a );
    }

    for (i = 0; i < 5; i++) {
        console.log("HERE", i); 

        it("should add for " + i, function() {

            checkForSituation(i);

        });

    }

});

and foo is just:

function foo(a, b) {
    return a + b;
}

I would expect it to check for 0 to 4, and print out

HERE 0
THERE 0
HERE 1
THERE 1
  ...

but instead, it just print in Chrome's console as: HERE 0, HERE 1, ... and then THERE 5 five times. Does somebody know why an expect cannot be put in an outside function like that and what to do in Jasmine if I need to put many steps into a function?

As a side note, sometimes in JavaScript, I feel like a whole new language is developed and what you can usually do won't work -- and wonder what can be done to prevent this type of things from happening, something without knowing that it would happen.

If you'd like to try it out, it is on https://github.com/jianlin/jasmine-looping-an-it-calling-function-that-does-expect

@sheelc
Owner

Hi @jianlin

Continuing the response you received on Stack Overflow, the reason you are seeing THERE 5 5 times is indeed a closure issue. To have THERE be called with 0-4, one way is to create a closure for the function that you are passing to Jasmine, by modifying the for loop as follows:

for (i = 0; i < 5; i++) {
  console.log("HERE", i); 

  it("should add for " + i, (function(i) {

    return function() { checkForSituations(i); };

  })(i));

}

As you noted, there wouldn't be a need for a closure if Jasmine executed the function passed immediately. One compelling, albeit simple, reason for the deferred execution is so that Jasmine knows how many specs there are before executing them. This allows for progress formatters that how far you are from having your suite complete.

The other side effect of the deferred execution that you noted is that all the "HERE"s are grouped together followed by "THERE"s. In most cases when writing specs programmatically, it should only matter that the expectations happen with the right arguments ("THERE" has 0-4). If interleaving "HERE"s and "THERE"s is important to your specs, one solution is to put the "it" on the outside with the for loop on the inside.

This pattern of execution for testing frameworks is not unique to Jasmine, either. Here is approximately the same test in Ruby with RSpec:

describe "TryTry" do
  def checkForSituations(a)
    puts "THERE", a
    foo(3, a).should == (3 + a)
  end

  for i in 1..5
    puts "HERE", a
    it "should add for #{i}" do
      checkForSituations(a)
    end
  end
end

Running this outputs the same block of "HERE"s and then "THERE"s as Jasmine (as well as the closure issue of THERE 5 5 times). In this case, switching the for loop to a Ruby each gets a closure.

Hopefully this explanation clears some things up!

@jianlin

So pretty much, it is because all those functions are added to a list, and then all executed later on, so that's why they all see the same, final i, which is 5.

I also might consider this form, so that it looks less complicated:

    for (i = 0; i < 5; i++) {

        (function(i) {
            console.log("HERE", i); 

            it("should add for " + i, function() {
                checkForSituations(i); 
            });
        }(i));

    }
@sheelc
Owner

Yep, that's pretty much it. Sure, that seems like another way to create the necessary closure.

@infews
Owner

Can we close this? It's not a Jasmine issue.

@sheelc
Owner

I'll close this and @jianlin if you have any further questions, please don't hesitate to ask on the mailing list.

@sheelc sheelc closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.