Vlad Vlad - 1 month ago 26
Javascript Question

Cannot use parametrize plugin for QUnit

Cannot run a simple parameterized test:

qunit.cases([{a: 1, b: 2}]).test("my test", function (params, assert) {
var sum = params.a + params.b;

assert.equal(3, sum, "correct");
});


It says that the callback function is null, but it isn't

qunit parameterized test

According to parameterize.js docs: parameterize plugin for qunit

I should do this:

QUnit
.cases(testCasesList)
.test(title, [expect], callback);


I checked the code and if expect is null, second it gets initialized to the callback function. I even tried expect = 3 and callback as third parameter but it still gives the same error, callback function is null.

What could I be doing wrong?

What should be expect parameter, a function a number?

Answer

I guess this tool was made for previous version of QUnit. I made some debugging and got to a solution which is not complete but it does the job for me. When I have better update, will update this answer here:

/*
 * Parameterize v 0.4
 * A QUnit Addon For Running Parameterized Tests
 * https://github.com/AStepaniuk/qunit-parameterize
 * Released under the MIT license.
 */
QUnit.extend(QUnit, {
    cases: (function() {
    'use strict';
    var currentCases = null,
        clone = function(testCase) {
            var result = {},
                p = null;

            for (p in testCase) {
                if (testCase.hasOwnProperty(p)) {
                    result[p] = testCase[p];
                }
            }

            return result;
        },
        createTest = function(methodName, title, expected, callback, parameters) {

            QUnit[methodName](title + ", test params: " + JSON.stringify(parameters), function(assert) {
                return callback.call(this, parameters, assert);
            });
        },

        iterateTestCases = function(methodName, title, expected, callback) {
            var i = 0,
                parameters = null,
                testCaseTitle = null;

            if (!currentCases || currentCases.length === 0) {
                // setup test which will always fail
                QUnit.test(title, function(assert) {
                    assert.ok(false, "No test cases are provided");
                });
                return;
            }

            if (!callback) {
                callback = expected;
                expected = null;
            }

            for (i = 0; i < currentCases.length; i += 1) {
                parameters = currentCases[i];

                testCaseTitle = title;
                if (parameters.title) {
                    testCaseTitle += "[" + parameters.title + "]";
                }

                createTest(methodName, testCaseTitle, expected, callback, parameters);
            }
        },

        getLength = function(arr) {
            return arr ? arr.length : 0;
        },

        getItem = function(arr, idx) {
            return arr ? arr[idx] : undefined;
        },

        mix = function(testCase, mixData) {
            var result = null,
                p = null;

            if (testCase && mixData) {
                result = clone(testCase);

                for (p in mixData) {
                    if (mixData.hasOwnProperty(p)) {
                        if (p !== "title") {
                            if (!(result.hasOwnProperty(p))) {
                                result[p] = mixData[p];
                            }
                        } else {
                            result[p] = [result[p], mixData[p]].join("");
                        }
                    }
                }

            } else if (testCase) {
                result = testCase;
            } else if (mixData) {
                result = mixData;
            } else {
                // return null or undefined whatever testCase is
                result = testCase;
            }

            return result;
        };

    return {

        init: function(testCasesList) {
            currentCases = testCasesList;
            return this;
        },

        sequential: function(addData) {
            var casesLength = getLength(currentCases),
                addDataLength = getLength(addData),
                length = casesLength > addDataLength ? casesLength : addDataLength,
                newCases = [],
                i = 0,
                currentCaseI = null,
                dataI = null,
                newCase = null;

            for (i = 0; i < length; i += 1) {
                currentCaseI = getItem(currentCases, i);
                dataI = getItem(addData, i);
                newCase = mix(currentCaseI, dataI);

                if (newCase) {
                    newCases.push(newCase);
                }
            }

            currentCases = newCases;

            return this;
        },

        combinatorial: function(mixData) {
            var current = (currentCases && currentCases.length > 0) ? currentCases : [null],
                currentLength = current.length,
                mixDataLength = 0,
                newCases = [],
                i = 0,
                j = 0,
                currentCaseI = null,
                dataJ = null,
                newCase = null;

            mixData = (mixData && mixData.length > 0) ? mixData : [null];
            mixDataLength = mixData.length;

            for (i = 0; i < currentLength; i += 1) {
                for (j = 0; j < mixDataLength; j += 1) {
                    currentCaseI = current[i];
                    dataJ = mixData[j];
                    newCase = mix(currentCaseI, dataJ);

                    if (newCase) {
                        newCases.push(newCase);
                    }
                }
            }

            currentCases = newCases;

            return this;
        },

        test: function(title, expected, callback) {
            iterateTestCases("test", title, expected, callback);
            return this;
        },

        getCurrentTestCases: function () {
            return currentCases;
        }
    };
    }())
});

First and foremost, I guess the author wanted to extend QUnit with 4 additional functions:

sequential(addData);
combinatorial(mixData);
test(title, expected, callback);
asyncTest(title, expected, callback);

but failed to do so. What I did is, I turned "cases" into object instead of a function and added init() function which initializes test cases array internally.

I also changed createTest() function to:

createTest = function(methodName, title, expected, callback, parameters) {
    QUnit[methodName](title + ", test params: " + JSON.stringify(parameters), function(assert) {
        return callback.call(this, parameters, assert);
    });
}

which calls QUnit.test(title, callback) directly without passing "expected" parameter. Not sure what was intended with this "expected" parameter but you can add your own parameters inside the test cases array and still cover what should be expected.

Here is an example how I create 3 parametrized tests:

QUnit.cases
        .init([{a: 1, b: 2, expected: 3}, {a: 4, b: 5, expected: 9}, {a: -5, b: 5, expected: 0}])
        .test("test sum(a, b)", function (parameters, assert) {
            var sum = parameters.a + parameters.b;

            assert.equal(sum, parameters.expected, "expected: " + parameters.expected + ", calculated: " + sum);
        });

Current script covers sequential testing but I left this function anyway:

qunit
    .cases
    .init([
        {param1: 50},
        {param1: 200},
        {param1: 300}
    ])
    .sequential([
        {param2: 100},
        null,
        {param2: 150}
    ])
    .test("Meta tests for QUnit Parametrize plugin", function (params, assert) {
        console.dir(params);
        assert.equal(params.param1, params.param2,"");
    });

You can also create combinatorial tests, which creates combinations of the tests in the init() and in the combinatorial() params segment

qunit
    .cases
    .init([
        {param1: 50}
    ])
    .combinatorial([
        {param2: 100},
        {param2: 150},
        {param2: 50}
    ])
    .test("Meta tests for QUnit Parametrize plugin", function (params, assert) {
        assert.equal(params.param1, params.param2,"");
    });

QUnit.async() is not supported in latest version. You should use QUnit.test() for async testing as well. Consult: qunit 2.x upgrade I removed async from QUnit Parameteize. Thanks for reading :)