Mocha: it in for loop

Created on 19 Oct 2017  路  4Comments  路  Source: mochajs/mocha

Hi everyone I'm tryin to execute 'it' in for loop. I'm changing a part of my test model at random. But 陌t works after for loop finished. But I want to execute it every iteration with different model. Here is my code:

describe('', () => {
const objNames = Object.getOwnPropertyNames(myModel);
    const len = objNames.length;
    for (let i = 0; i < len; i++) {
        const rnd = (Math.floor(Math.random() * len) + 0);
        const model = myModel;
        console.log('loop')
        model[objNames[rnd]] = null;
        it('', () => {
            //console.log(JSON.stringify(model)) // Everytime getting same model ( I think the last one)
            // Do something
        });
    }
});

screen shot 2017-10-19 at 11 49 55

question

Most helpful comment

Hi @spolat,

As you have found, Mocha does not run the callback in it immediately, only after it has run all the test files and found out what all their hooks and tests are.

However, it appears that the main issue here is that the variable in the for loop is not distinct for each it created. This is because JavaScript updates the variable used in the loop rather than creating a new variable each time, so all of the its are referencing the same variable. There are a couple ways to fix this:

  • Use an immediately-invoked function expression (IIFE) inside the loop to copy each value as a separate parameter:
for (var index = 0; index < array.length; index += 1) (function(arrayEntry, indexIfYouNeedIt) {
  it("", function() {
    console.log(arrayEntry)
    //console.log(indexIfYouNeedIt)
  })
})(array[index], index))
  • Use the array forEach method:
array.forEach(function(arrayEntry, indexIfYouNeedIt) {
  it("", function() {
    console.log(arrayEntry)
    //console.log(indexIfYouNeedIt)
  })
})

I don't know about you, but I find the forEach method a lot cleaner and harder to screw up too. If you need to run this on things that can be accessed like arrays but don't have forEach because they aren't actually arrays, it's still possible to use the cleaner forEach method -- just use [].forEach.call(arrayLikeThing, <the usual arguments to forEach>) instead. If you need to run it in really old environments that don't have forEach on arrays and/or don't have call, it's possible to use a standalone version like this:

function forEach(array, operation) {
  for (var index = 0; index < array.length; index += 1) {
    operation(array[index], index)
  }
}

...although there are libraries out there such as "es5shim" that provide implementations of forEach that more closely match the details of the array method's full behavior.

Please let us know if that solves your issue!

All 4 comments

Hi @spolat,

As you have found, Mocha does not run the callback in it immediately, only after it has run all the test files and found out what all their hooks and tests are.

However, it appears that the main issue here is that the variable in the for loop is not distinct for each it created. This is because JavaScript updates the variable used in the loop rather than creating a new variable each time, so all of the its are referencing the same variable. There are a couple ways to fix this:

  • Use an immediately-invoked function expression (IIFE) inside the loop to copy each value as a separate parameter:
for (var index = 0; index < array.length; index += 1) (function(arrayEntry, indexIfYouNeedIt) {
  it("", function() {
    console.log(arrayEntry)
    //console.log(indexIfYouNeedIt)
  })
})(array[index], index))
  • Use the array forEach method:
array.forEach(function(arrayEntry, indexIfYouNeedIt) {
  it("", function() {
    console.log(arrayEntry)
    //console.log(indexIfYouNeedIt)
  })
})

I don't know about you, but I find the forEach method a lot cleaner and harder to screw up too. If you need to run this on things that can be accessed like arrays but don't have forEach because they aren't actually arrays, it's still possible to use the cleaner forEach method -- just use [].forEach.call(arrayLikeThing, <the usual arguments to forEach>) instead. If you need to run it in really old environments that don't have forEach on arrays and/or don't have call, it's possible to use a standalone version like this:

function forEach(array, operation) {
  for (var index = 0; index < array.length; index += 1) {
    operation(array[index], index)
  }
}

...although there are libraries out there such as "es5shim" that provide implementations of forEach that more closely match the details of the array method's full behavior.

Please let us know if that solves your issue!

@ScottFreeCode thanks for your reply. I solved by execute all codes in it scopes. Thanks.

So how can we actually execute it statement for each loop iteration? it is ignored inside loop.
Should we only do common checking for all array members?

Please don't comment on closed issues if you need help...
See the documentation.

Was this page helpful?
0 / 5 - 0 ratings