Mocha:以编程方式创建并完成测试

时间:2016-01-19 09:54:11

标签: javascript node.js unit-testing mocha

我想实时代理在其他环境中运行的测试结果。

这是我想要实现的一些伪代码:

  var test = proxy.getCurrentTest(); 
  // => {slow: 200, timeout: 2000, duration: 235, result: 'error'};

  var tmpIt = it('test1', function(){
      this.slow(test.slow);
      this.timeout(test.timeout);
  });
  tmpIt.close({
      duration: test.duration,
      result: test.result
  });
  // this should make this test red in the output, 
  // because the `result` is not 'success'

无论如何可以设置测试的结果和持续时间,而不是"真的"跑吧?并将所有可视的mocha输出到终端?

编辑:这个问题不是关于如何将带有子进程的测试结果的变量传递给主进程。它已经为我工作了。

1 个答案:

答案 0 :(得分:3)

希望我能正确理解要求。我实现的是mocha的测试结果转发器,它集成到mocha中。

要与mocha集成,此实现描述了custom mocha interface来代理在另一个环境中执行的测试的测试结果。

要使用此接口,运行mocha时必须将-u参数传递给mocha

> mocha -u ./path/to/proxy-interface ...

请注意,./path/to/proxy-interface是mocha在require调用中用于要求接口模块的路径。

代理接口负责向全局上下文公开proxyTest函数,例如mocha的BDD接口与it一起,调用传递的函数来获取测试结果并转发测试结果,同时保留测试运行员显示的测试次数。

var Mocha = require('mocha');

var Suite = Mocha.Suite;
var Test = Mocha.Test;
var escapeRe = require('escape-string-regexp');

module.exports = function(suite) {
  var suites = [suite];

  suite.on('pre-require', function(context, file, mocha) {
    // A bit hacky since we require mocha internal common interface module
    var common = require('mocha/lib/interfaces/common')(suites, context);

    context.run = mocha.options.delay && common.runWithSuite(suite);

    context.proxyTest = function(title, fn) {
      var suite = suites[0];
      if (suite.pending) {
        fn = null;
      }
      var test = new ProxyTest(title, fn);
      test.file = file;
      suite.addTest(test);
      return test;
    };
  });
};

var Runnable = Mocha.Runnable;
var inherits = require('util').inherits;

function ProxyTest(title, fn) {
  Runnable.call(this, title, null);
  this.pending = !fn;
  this.type = 'test';
  this.body = (fn || '').toString();

  this.fn = fn;
}

inherits(ProxyTest, Runnable);

ProxyTest.prototype.run = function(done) {
  var proxiedTestResult = this.fn();

  this.duration = proxiedTestResult.duration;
  this.timedOut = this.timeout() > proxiedTestResult.timeout;

  done(proxiedTestResult.result);
};

ProxyTest.prototype.clone = function() {
  var test = new ProxyTest(this.title, this.fn);
  test.timeout(this.timeout());
  test.slow(this.slow());
  test.enableTimeouts(this.enableTimeouts());
  test.retries(this.retries());
  test.currentRetry(this.currentRetry());
  test.globals(this.globals());
  test.parent = this.parent;
  test.file = this.file;
  test.ctx = this.ctx;
  return test;
};

上面的代码覆盖了Mocha的Runnable运行实现,并运行传递的函数来获取测试结果,并设置ProxyTest接口中的必需字段以与mocha测试兼容。

<强>用法

在您的测试中,使用proxyTest全局来使用mocha注册新测试

var proxy = {
  getErrorTestResult() {
    return {slow: 200, timeout: 2000, duration: 50, result: 'error'};
  },

  getTimeoutTestResult() {
    return {slow: 200, timeout: 2000, duration: 3000 };
  },

  getSlowTestResult() {
    return {slow: 200, timeout: 2000, duration: 235 };
  },

  getSuccessTestResult() {
    return {slow: 200, timeout: 2000, duration: 50 };
  }
}

proxyTest('error', proxy.getErrorTestResult);
proxyTest('timeout', proxy.getTimeoutTestResult);
proxyTest('slow', proxy.getSlowTestResult);
proxyTest('success', proxy.getSuccessTestResult);

<强>输出

Mocha Output

<强>启示

这种方法的缺点是必须将自定义界面传递给mocha AND ,您不能使用像describe这样的mocha BDD词汇表。如果你&#34;延伸&#34;可以消除第二个缺点。 (在这种情况下:复制一些代码)mocha的BDD接口:

var Mocha = require('mocha');
/**
 * Module dependencies.
 */

var Suite = Mocha.Suite;
var Test = Mocha.Test;
var escapeRe = require('escape-string-regexp');

/**
 * BDD-style interface - extended with proxy functionality:
 *
 *      describe('Array', function() {
 *        describe('#indexOf()', function() {
 *          it('should return -1 when not present', function() {
 *            // ...
 *          });
 *
 *          it('should return the index when present', function() {
 *            // ...
 *          });
 *        });
 *      });
 *
 * @param {Suite} suite Root suite.
 */
module.exports = function(suite) {
  var suites = [suite];

  suite.on('pre-require', function(context, file, mocha) {
    // A bit hacky since we require mocha internal common interface module
    var common = require('mocha/lib/interfaces/common')(suites, context);

    context.before = common.before;
    context.after = common.after;
    context.beforeEach = common.beforeEach;
    context.afterEach = common.afterEach;
    context.run = mocha.options.delay && common.runWithSuite(suite);
    /**
     * Describe a "suite" with the given `title`
     * and callback `fn` containing nested suites
     * and/or tests.
     */

    context.describe = context.context = function(title, fn) {
      var suite = Suite.create(suites[0], title);
      suite.file = file;
      suites.unshift(suite);
      fn.call(suite);
      suites.shift();
      return suite;
    };

    /**
     * Pending describe.
     */

    context.xdescribe = context.xcontext = context.describe.skip = function(title, fn) {
      var suite = Suite.create(suites[0], title);
      suite.pending = true;
      suites.unshift(suite);
      fn.call(suite);
      suites.shift();
    };

    /**
     * Exclusive suite.
     */

    context.describe.only = function(title, fn) {
      var suite = context.describe(title, fn);
      mocha.grep(suite.fullTitle());
      return suite;
    };

    /**
     * Describe a specification or test-case
     * with the given `title` and callback `fn`
     * acting as a thunk.
     */

    var it = context.it = context.specify = function(title, fn) {
      var suite = suites[0];
      if (suite.pending) {
        fn = null;
      }
      var test = new Test(title, fn);
      test.file = file;
      suite.addTest(test);
      return test;
    };

    /**
     * Exclusive test-case.
     */

    context.it.only = function(title, fn) {
      var test = it(title, fn);
      var reString = '^' + escapeRe(test.fullTitle()) + '$';
      mocha.grep(new RegExp(reString));
      return test;
    };

    /**
     * Pending test case.
     */

    context.xit = context.xspecify = context.it.skip = function(title) {
      context.it(title);
    };

    /**
     * Number of attempts to retry.
     */
    context.it.retries = function(n) {
      context.retries(n);
    };

    context.proxyTest = function(title, fn) {
      var suite = suites[0];
      if (suite.pending) {
        fn = null;
      }
      var test = new ProxyTest(title, fn);
      test.file = file;
      suite.addTest(test);
      return test;
    };
  });
};

var Runnable = Mocha.Runnable;
var inherits = require('util').inherits;

function ProxyTest(title, fn) {
  Runnable.call(this, title, null);
  this.pending = !fn;
  this.type = 'test';
  this.body = (fn || '').toString();

  this.fn = fn;
}

inherits(ProxyTest, Runnable);

ProxyTest.prototype.run = function(done) {
  var proxiedTestResult = this.fn();

  this.duration = proxiedTestResult.duration;
  this.timedOut = this.timeout() > proxiedTestResult.timeout;

  done(proxiedTestResult.result);
};

ProxyTest.prototype.clone = function() {
  var test = new ProxyTest(this.title, this.fn);
  test.timeout(this.timeout());
  test.slow(this.slow());
  test.enableTimeouts(this.enableTimeouts());
  test.retries(this.retries());
  test.currentRetry(this.currentRetry());
  test.globals(this.globals());
  test.parent = this.parent;
  test.file = this.file;
  test.ctx = this.ctx;
  return test;
};