Mocha test case – are nested it( ) functions kosher?

I have this case where I think I want to have nested it() test cases in a Mocha test. I am sure this is wrong, and I don’t see any recommendations to do what I am doing, but I don’t really know of a better way at the moment –

basically, I have a “parent” test, and inside the parent test there’s a forEach loop with all the “child” tests:

it('[test] enrichment', function (done) {

        var self = this;

        async.each(self.tests, function (json, cb) {

            //it('[test] ' + path.basename(json), function (done) {

                var jsonDataForEnrichment = require(json);
                jsonDataForEnrichment.customer.accountnum = "8497404620452729";
                jsonDataForEnrichment.customer.data.accountnum = "8497404620452729";
                var options = {
                    url: self.serverURL + ':' + self.serverPort + '/event',
                    json: true,
                    body: jsonDataForEnrichment,
                    method: 'POST'
                };


               request(options,function (err, response, body) {
                    if (err) {
                        return cb(err);
                    }

                     assert.equal(response.statusCode, 201, "Error: Response Code");
                     cb(null);


                });

            //});

        }, function complete(err) {
            done(err)
        });

    });

as you can see, two separate lines are commented out – I want to include them so that I can easily see the results of each separate test, but then I have this awkward situation of firing the callback for the test alongside the callback for async.each.

Has anyone seen this time of situation before and know of a good solution where the tester can easily see the results of each test in a loop?

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

Don’t nest it calls. Call them synchronously.

Nested it calls are never okay in Mocha. Nor are it calls performed asynchronously. (The test can be asynchronous, but you cannot call it asynchronously.) Here’s a simple test:

describe("level 1", function () {
    describe("first level 2", function () {
        it("foo", function () {
            console.log("foo");
            it("bar", function () {
                console.log("bar");
            });
        });

        setTimeout(function () {
            it("created async", function () {
                console.log("the asyncly created one");
            });
        }, 500);
    });

    describe("second level 2", function () {
        // Give time to the setTimeout above to trigger.
        it("delayed", function (done) {
            setTimeout(done, 1000);
        });
    });
});

If you run this you won’t get the nested test bar will be ignored and the test created asynchronously (delayed) will also be ignored.

Mocha has no defined semantics for these kinds of calls. When I ran my test with the latest version of Mocha at the time of writing (2.3.3), it just ignored them. I recall that an earlier version of Mocha would have recognized the tests but would have attached them to the wrong describe block.

Solution 2

I think the need for dynamic tests are relatively common (data-driven tests?), and there is common use for dynamic it and test cases.

I think it could be easier to manage testcase completion if tests could be executed in series. This way you wouldn’t have to worry about managing nested async done‘s. Since request is async (i’m assuming), your test cases will still mainly be executing concurrently.

describe('[test] enrichment', function () {

        var self = this;


        _.each(self.tests, function (json, cb) {

            it('[test] ' + path.basename(json), function (done) {

                var jsonDataForEnrichment = require(json);
                jsonDataForEnrichment.customer.accountnum = "8497404620452729";
                jsonDataForEnrichment.customer.data.accountnum = "8497404620452729";
                var options = {
                    url: self.serverURL + ':' + self.serverPort + '/event',
                    json: true,
                    body: jsonDataForEnrichment,
                    method: 'POST'
                };


               request(options,function (error, response, body) {
                    if (error) {
                        cb(error);

                    }
                    else{
                        assert.equal(response.statusCode, 201, "Error: Response Code");
                        cb(null);
                    }

                    done(); 
                });

            });

        }
    });

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply