30
votes

The nose testing framework (for python) supports dynamically generating test cases at run-time (the following, from the documentation, results in five distinct test cases):

def test_evens():
    for i in range(0, 5):
        yield check_even, i, i*3

def check_even(n, nn):
    assert n % 2 == 0 or nn % 2 == 0

How can I achieve this result using javascript frameworks such as mocha or qunit? (I am not attached to any particular framework at this point.)

My use-case is writing a test runner to monitor several items on an external server. I would provide a list of resource URLs. Each test attempts to poll that resource and returns success or failure depending on what it finds. I have a prototype built in python (using nose) but would like to implement in node.js if I can. Eventually, this would be included in a CI setup.

7
node as in node.js? Maybe you should tag it, since just javascript will be interpreted as meaning javascript in browser. - developerwjk

7 Answers

35
votes

Yes you can dynamically created test suites with cases using Mocha. I have installed mocha globally npm install -g mocha and I use should.

var should = require('should');

var foo = 'bar';

['nl', 'fr', 'de'].forEach(function(arrElement) {
  describe(arrElement + ' suite', function() {
    it('This thing should behave like this', function(done) {
      foo.should.be.a.String();
      done();
    });
    it('That thing should behave like that', function(done) {
      foo.should.have.length(3);
      done();
    });
  });
});
31
votes

If you want to dynamically create It() tests using data obtained asynchronously, you can (ab)use the before() hook with a placeholder It() test to ensure mocha waits until before() is run. Here's the example from my answer to a related question, for convenience:

before(function () {
    console.log('Let the abuse begin...');
    return promiseFn().
        then(function (testSuite) {
            describe('here are some dynamic It() tests', function () {
                testSuite.specs.forEach(function (spec) {
                    it(spec.description, function () {
                        var actualResult = runMyTest(spec);
                        assert.equal(actualResult, spec.expectedResult);
                    });
                });
            });
        });
});

it('This is a required placeholder to allow before() to work', function () {
    console.log('Mocha should not require this hack IMHO');
});
11
votes

With Mocha 1.21.4, you can create suite/test at runtime in following way.

require('chai').should()

Mocha = require 'mocha'
Test = Mocha.Test
Suite = Mocha.Suite


mocha = new Mocha
suite = Suite.create mocha.suite, 'I am a dynamic suite'
suite.addTest new Test 'I am a dynamic test', ->
  true.should.equal true

mocha.run () ->
  console.log("done")

See https://gist.github.com/cybertk/fff8992e12a7655157ed for more details

11
votes

It's worth noting that in addition to the accepted answer above, mocha's docs now include an example of how to achieve this. I've reproduced it below for posterity.

var assert = require('assert');

function add() {
  return Array.prototype.slice.call(arguments).reduce(function(prev, curr) {
    return prev + curr;
  }, 0);
}

describe('add()', function() {
  var tests = [
    {args: [1, 2],       expected: 3},
    {args: [1, 2, 3],    expected: 6},
    {args: [1, 2, 3, 4], expected: 10}
  ];

  tests.forEach(function(test) {
    it('correctly adds ' + test.args.length + ' args', function() {
      var res = add.apply(null, test.args);
      assert.equal(res, test.expected);
    });
  });
});
5
votes

Yep! Brilliant advice from Quanlong!

Here is my example of dynamic test generation with Node's readline module:

const Mocha = require('mocha');
var Test = Mocha.Test;
var Suite = Mocha.Suite;

var mocha = new Mocha();
var suite = Suite.create(mocha.suite, 'My test suite with dynamic test cases');

lineReader
    .on('line', function (line) {
        suite.addTest(new Test(line, function () {
            return true;
        }));
    })
    .on('close', function () {
        mocha.run();
    });
2
votes

I like @rob3c's answer, but tried to simplify it a bit:

describe("Master test suite", function () {
  before(async function () {
    const rows = await mySQLQuery();

    describe(`Testing ${rows.length} rows`, function () {
      rows.forEach(function (row, index) {
        it(`Test row ${index}`, async function() {
          console.log("your row assertions go here")
        });
      });
    });
  });


  it("stub", async function(){})  // this is important!
});
0
votes

You can accomplish this by updating the tests property manually after the response is returned from the async method:

describe(`sometest`, function() {
  let results = null
  before(async () => {
    results = await someAsyncMethod();
    results.forEach((result, index) => {
      // to hold on to the new dynamic tests
      const newTest = it(result.name || `test ${index}`, () => {
        // do something here in test
      });
      // update the test objects before the main tests run
      this.tests.push(newTest);
    });
  });

  it(`sometest`, () => {
    expect(results.length).toBeGreaterThan(2);
  });

});

This doesn't use dynamic describes etc, just updates the current describe block before the main tests run!