运行 整个 mocha 测试之前的异步代码

Run async code before entire mocha test

我正在寻找一种在整个 mocha 测试之前 运行 异步代码的方法。

下面是一个测试示例,它使用一组参数和期望并循环遍历该数组中的所有项目以生成函数断言。

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)
    })
  })
})

现在,我的问题是,如果测试值来自承诺,这将如何工作,如下所示:

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)
      })
    })
  })  
})

也尝试过:

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)
      })
    })
  })
})

但是在本示例中 none 的测试 运行 因为 mocha 无法识别 describe 语句,因为它在 promise 中。

before / beforeEach 无论如何都不会做任何事情来帮助测试格式,除非是一个 beforeTest 钩子,它可以让 mocha 知道有一个在整个测试之前需要 运行 的异步操作。

我不确定是否有任何简单的方法可以做到这一点,但您可以尝试 run Mocha programatically

这是一个有点脏的版本,只是为了展示这个想法。

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)
      })
    })
  })
})

然后你可以运行你在运行宁test-launcher.js之前休息。

我会在 it 调用中移动异步逻辑。太喜欢单元测试是一种代码味道,可能会激怒其他开发人员,因为他们不仅要调试和修复失败的测试,还要调试和修复甚至没有定义的测试 运行 因为花哨设置代码有错误。尽量不要去那里。

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'
        }
      ]
    })
}

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

作为 Daniel Perez 方法的替代方法,您还可以 use the command line switch --delay and start the tests on the first run() call。通过异步延迟 run(),您可以预先异步注册 describes 和 its。但是请注意,您只能调用 run() 一次,即只能在一个测试文件中调用。因此,我在 ./test/ 中创建了一个异步测试运行器,在 ./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);
});

// ./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;

我会使用 async/await 和 delay option,如下所示:

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)