Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run async code before entire mocha test

I'm looking for a way to run async code before the entire mocha test.

Here's an example of a test that uses an array of arguments & expectations and loops over all of the items in this array to produce function assertions.

var assert = require('assert')

/* global describe, it*/

var fn = function (value) {
  return value + ' ' + 'pancake'
}

var tests = [
  {
    'arg': 'kitty',
    'expect': 'kitty pancake'
  },
  {
    'arg': 'doggy',
    'expect': 'doggy pancake'
  },
]

describe('example', function () {
  tests.forEach(function (test) {
    it('should return ' + test.expect, function (){
      var value = fn(test.arg)
      assert.equal(value, test.expect)
    })
  })
})

Now, my question is how would this work if the tests value came from a promise, like this:

var assert = require('assert')
var Promise = require('bluebird')

/* global describe, it*/

var fn = function (value) {
  return value + ' ' + 'pancake'
}

function getTests () {
  return Promise.resolve('kitty pancake')
  .delay(500)
  .then(function (value) {
    return [
      {
        'arg': 'kitty',
        'expect': value
      },
      {
        'arg': 'doggy',
        'expect': 'doggy pancake'
      }
    ]
  })
}

getTests().then(function (tests) {
  describe('example', function () {
    tests.forEach(function (test) {
      it('should return ' + test.expect, function (){
        var value = fn(test.arg)
        assert.equal(value, test.expect)
      })
    })
  })  
})

also tried:

describe('example', function () {
  getTests().then(function (tests) {
    tests.forEach(function (test) {
      it('should return ' + test.expect, function (){
        var value = fn(test.arg)
        assert.equal(value, test.expect)
      })
    })
  })
})

However in this example none of the tests run because mocha doesn't recognize the describe statement because it's within a promise.

before / beforeEach won't do anything to help with a test in the format anyway unless the was a beforeTest hook that would supply mocha with the knowledge that there's an async operation that needs to be run before the entire test.

like image 658
ThomasReggi Avatar asked Sep 03 '15 03:09

ThomasReggi


People also ask

How do you test async in Mocha?

Writing Asynchronous Tests with Mocha. To indicate that a test is asynchronous in Mocha, you simply pass a callback as the first argument to the it() method: it('should be asynchronous', function(done) { setTimeout(function() { done(); }, 500); });

Are Mocha tests sequential?

Mocha will run tests sequentially, waiting for each to finish before starting the next one.

How do you skip a Mocha test?

You can skip tests by placing an x in front of the describe or it block, or placing a . skip after it. describe('feature 1', function() {}); describe.


3 Answers

As an alternative to Daniel Perez's method you can also use the command line switch --delay and start the tests on the first run() call. By delaying the run() asynchronously you can register describes and its asynchronously beforehand. Note, though, that you can only call run() once, i.e. only in one test file. Thus I've created an async test runner in ./test/ and each async test in ./testAsync/:

// ./test/asyncRunner.js
"use strict";

const allAsyncPaths = [
  "test-featureA",
  "test-featureB",
].map(a => "../testAsync/" + a);

const allAsyncTestFunctions = allAsyncPaths.map(require);

Promise.resolve({
}).then(function() {
  const allPromises = allAsyncTestFunctions.map(a => a());
  return Promise.all(allPromises);
}).then(function() {
  run(); // mocha waits for run() because of --delay flag
}).catch(function(err) {
  console.error(err);
});

and

// ./testAsync/test-featureA.js
"use strict";
function asyncTestRegistrator() {

  return Promise.resolve({
  }).then(function() {
    return getTestsAsync();
  }).then(function(tests) {

  describe('example', function () {
    tests.forEach(function (test) {
      it('should return ' + test.expect, function (){
        var value = fn(test.arg);
        assert.equal(value, test.expect);
      });
    });
  });
}
module.exports = asyncTestRegistrator;
like image 130
Perseids Avatar answered Sep 30 '22 17:09

Perseids


I am not sure if there is any easy way to do this, but you could try to run Mocha programatically.

Here is a little dirty version of what this could look like, just to show the idea.

data.js

var Promise = require('bluebird')

module.exports.tests = []

function getTests () {
  return Promise.resolve('kitty pancake')
  .delay(500)
  .then(function (value) {
     module.exports.tests = [
      {
        'arg': 'kitty',
        'expect': value
      },
      {
        'arg': 'doggy',
        'expect': 'doggy pancake'
      }
    ]
  })
}

module.exports.getTests = getTests

test-launcher.js

var Mocha = require('mocha'),
    fs = require('fs'),
    path = require('path')

// First, you need to instantiate a Mocha instance.
var mocha = new Mocha()

// Then, you need to use the method "addFile" on the mocha
// object for each file.

// Here is an example:
fs.readdirSync('test').filter(function(file){
    // Only keep the .js files
    return file.substr(-3) === '.js'

}).forEach(function(file){
    // Use the method "addFile" to add the file to mocha
    mocha.addFile(
        path.join('test', file)
    )
})

// make sure your tests are loaded before running the tests
require('./data').getTests().then(function () {

  // Now, you can run the tests.
  mocha.run(function(failures){
    process.on('exit', function () {
      process.exit(failures)
    })
  })
})

test/index.js

var assert = require('assert')

var tests = require('../data').tests

var fn = function (value) {
  return value + ' ' + 'pancake'
}

describe('test', function () {
  describe('example', function () {
    tests.forEach(function (test) {
      it('should return ' + test.expect, function (){
        var value = fn(test.arg)
        assert.equal(value, test.expect)
      })
    })
  })
})

You can then run you rests by running test-launcher.js.

like image 25
Daniel Perez Avatar answered Sep 30 '22 15:09

Daniel Perez


I would use the async/await with delay option as below:

setTimeout(async () => {
//get tests async
const tests = await getTests()

describe('example', async () => {

  tests.forEach((test) => {
   it(`test name: ${test.name} `, () => {
    console.log(test.name)
  })
 })
})

run()
}, 1000)
like image 41
Quang Xuan Nguyen Avatar answered Sep 30 '22 16:09

Quang Xuan Nguyen