简体   繁体   中英

Using a for loop in a Mocha Test

I'm new to mocha, and I wanted to try using a for loop to create test cases. I want to test a function that I've made that takes an input of standard 12 hour time, and outputs it into 24 hour military time. This is what it looks like.

exports.main = function(time) {
var hr = parseInt(time.substr(0,2));
var period = time.substr(8,10);
if (period == 'AM' && hr == 12) {
    hr = '0';
}
if (period == 'PM' && hr < 12) {
           hr += 12;
}
    hr.toString();
    if (hr < 10) {
        hr = '0' + hr;
    }
 return time = time.replace(/^\d{2}/g, hr).substr(0,8);

}

To test my function in mocha, I have two arrays, one array holds the standard times and the other holds the corresponding expected output. I want to iterate through them and produce a test case for each iteration and test my function.

test_array = ["12:00:00AM", "01:00:00AM", "02:00:00AM", "03:00:00AM", "04:00:00AM",
"05:00:00AM", "06:00:00AM", "07:00:00AM", "08:00:00AM", "09:00:00AM",
"10:00:00AM", "11:00:00AM", "12:00:00PM", "01:00:00PM", "02:00:00PM",
"03:00:00PM", "04:00:00PM", "05:00:00PM", "06:00:00PM", "07:00:00PM",
"08:00:00PM", "09:00:00PM", "10:00:00PM", "11:00:00PM"];

against = ["00:00:00", "01:00:00", "02:00:00", "03:00:00", "04:00:00",
"05:00:00", "06:00:00", "07:00:00", "08:00:00", "09:00:00", "10:00:00",
"11:00:00", "12:00:00", "13:00:00", "14:00:00", "15:00:00", "16:00:00",
"17:00:00", "18:00:00", "19:00:00", "20:00:00", "21:00:00", "22:00:00",
"23:00:00"]

This is what my test script looks like:

var converter = require('../modules/time.js');
describe('Time Converter', function() {
 describe('main()', function() {
  for(i = 0; i < 24; i++) {
   it(test_array[i]  + ' should convert to ' + against[i], function() {
   var test = converter.main(test_array[i]);
   assert.equal(test, against[i]);
   });
  }
 });
});

The following is the results of the tests:

0 passing (23ms)
24 failing

1) Time Converter main() 12:00:00AM should convert to 00:00:00:
 TypeError: Cannot read property 'substr' of undefined
  at Object.exports.main (app/modules/time.js:43:27)
  at Context.<anonymous> (app/test/test.js:35:26)

2) - 24) has the same result:

24) Time Converter main() 11:00:00PM should convert to 23:00:00:
 TypeError: Cannot read property 'substr' of undefined
  at Object.exports.main (app/modules/time.js:43:27)
  at Context.<anonymous> (app/test/test.js:35:26)

However when I change the for loop to

for(i = 0; i < 23; i++)

All the tests pass except, naturally it doesn't test the last test case.

Time Converter
main()
  ✓ 12:00:00AM should convert to 00:00:00
  ✓ 01:00:00AM should convert to 01:00:00
  ✓ 02:00:00AM should convert to 02:00:00
  ✓ 03:00:00AM should convert to 03:00:00
  ✓ 04:00:00AM should convert to 04:00:00
  ✓ 05:00:00AM should convert to 05:00:00
  ✓ 06:00:00AM should convert to 06:00:00
  ✓ 07:00:00AM should convert to 07:00:00
  ✓ 08:00:00AM should convert to 08:00:00
  ✓ 09:00:00AM should convert to 09:00:00
  ✓ 10:00:00AM should convert to 10:00:00
  ✓ 11:00:00AM should convert to 11:00:00
  ✓ 12:00:00PM should convert to 12:00:00
  ✓ 01:00:00PM should convert to 13:00:00
  ✓ 02:00:00PM should convert to 14:00:00
  ✓ 03:00:00PM should convert to 15:00:00
  ✓ 04:00:00PM should convert to 16:00:00
  ✓ 05:00:00PM should convert to 17:00:00
  ✓ 06:00:00PM should convert to 18:00:00
  ✓ 07:00:00PM should convert to 19:00:00
  ✓ 08:00:00PM should convert to 20:00:00
  ✓ 09:00:00PM should convert to 21:00:00
  ✓ 10:00:00PM should convert to 22:00:00
  23 passing (14ms)

However when I test the last test case by itself, it passes.

✓ 11:00:00PMshould convert to 23:00:00

So, I'm confused as to why all the tests don't work if the for loop iterates through the whole array, but works if I iterate up until the last index. Can someone clear this up for me?

Your code may be sync, but mocha calls it it async. Means: your it descriptions are parsed sync, mocha stores this information and runs every test in it's own context. This is async. To make this work, you have to create a closure with a function:

var converter = require('../modules/time.js');
// outside the loop:
function itShouldTestArray(i) {
   // i is now within the function scope and won't change anymore
   it(test_array[i]  + ' should convert to ' + against[i], function() {
   var test = converter.main(test_array[i]);
   assert.equal(test, against[i]);
}
describe('Time Converter', function() {
 describe('main()', function() {
  for(i = 0; i < 24; i++) {
   itShouldTestArray(i);
   });
  }
 });
});

So, I'm confused as to why all the tests don't work if the for loop iterates through the whole array, but works if I iterate up until the last index. Can someone clear this up for me?

This is probably a very-hard-to-debug-problem caused by the asynchronous testing conducted by Mocha. It seems like that changing i from 23 to 24 somehow results in your arrays being deleted before the tests are conducted, confer the error message:

 TypeError: Cannot read property 'substr' of undefined

You can avoid this deletion of the arrays before testing time by assigning their values inside a closure:

var converter = require('../modules/time.js');

describe('Time Converter', function() {
  describe('main()', function() {

       test_array = // insert array inside closure;
       against = //insert array inside closure;

       callback = function () {

        var test = converter.main (test_array[i]);
        assert.equal(test, against[i]);
       }

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

          it(test_array[i]  + ' should convert to ' + against[i], callback);
        }
      })
    })

This way each callback passed to it() will have access to the arrays without any possibility of them being deleted or altered before testing time.

I don't know mocha at all, so I will defer to Johannes' expert advice on that.

I do have some other suggestions for you though.

First, it will be a real maintenance headache having two arrays running in parallel like test_array and against . It's hard to look at those and be sure the right values are matched up.

Instead, put everything into a single array, where each array element contains both test values:

var tests = [
    '12:00:00AM = 00:00:00',
    '01:00:00AM = 01:00:00',
    '02:00:00AM = 02:00:00',
    '03:00:00AM = 03:00:00',
    '04:00:00AM = 04:00:00',
    '05:00:00AM = 05:00:00',
    '06:00:00AM = 06:00:00',
    '07:00:00AM = 07:00:00',
    '08:00:00AM = 08:00:00',
    '09:00:00AM = 09:00:00',
    '10:00:00AM = 10:00:00',
    '11:00:00AM = 11:00:00',
    '12:00:00PM = 12:00:00',
    '01:00:00PM = 13:00:00',
    '02:00:00PM = 14:00:00',
    '03:00:00PM = 15:00:00',
    '04:00:00PM = 16:00:00',
    '05:00:00PM = 17:00:00',
    '06:00:00PM = 18:00:00',
    '07:00:00PM = 19:00:00',
    '08:00:00PM = 20:00:00',
    '09:00:00PM = 21:00:00',
    '10:00:00PM = 22:00:00',
    '11:00:00PM = 23:00:00',
];

You can use split() in your code to separate the two values in each array element.

Next, never hard code an array length in a for loop. Instead, if the array is named test as in my example above, use i < test.length in the loop. Now if you add or remove entries, the loop will still have the correct length.

However, I wouldn't use a for loop here at all. You can get cleaner code by using the forEach array method.

Taking Johannes' code as a starting point, I would change it to:

function itShouldTestTimes( test ) {
    var times = test.split( ' = ' );
    var time12 = times[0], time24 = times[1];
    it( time12 + ' should convert to ' + time24, function() {
        var result = converter.main( time12 );
        assert.equal( result, time24 );
    });
}

describe( 'Time Converter', function() {
    describe( 'main()', function() {
        tests.forEach( itShouldTestTimes );
    });
});

1) BatchTest.js

const assert = require('chai').assert;
const fileOperations = require('../UDRServer/Utility/FileOperations');
var testResultFileName = "TestResult.json";

var data = fileOperations.readFile(testResultFileName);
//console.log(data);

var jsons = data.split('\n');


function runTest(obj) {
    describe(obj.name + " Test", function () {
        it("Expexted Value " + obj.expectedStatus, function (done) {

            assert.equal(obj.expectedStatus, obj.actualStatus);
            if (obj.expectedCause != 'null') {
                assert.equal(obj.expectedCause, obj.actualCause);
            }
            done();
        })

    })
}


describe("Main", function () {
    for (let i = 0; i < jsons.length - 1; i++) {
        var obj = JSON.parse(jsons[i]);
        runTest(obj);
    }
});

2 ) FileOperations.js

var readFile = function(filename){
    return fs.readFileSync(filename).toString();
}

3) TestResult.json file

{"actualStatus":403,"actualCause":"false","name":"test1","expectedCause":"false","expectedStatus":400}
{"actualStatus":0,"actualCause":"true","name":"test2","expectedCause":"false","expectedStatus":400}
{"actualStatus":400,"actualCause":"false","name":"test3","expectedCause":"false","expectedStatus":400}
{"actualStatus":200,"actualCause":"true","name":"test4","expectedCause":"false","expectedStatus":200}

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