【问题标题】:Mocha: create and finalize test programmaticallyMocha:以编程方式创建和完成测试
【发布时间】:2016-04-24 17:39:03
【问题描述】:

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

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

  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 输出到终端?

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

【问题讨论】:

  • 如果我正确理解您的问题,您可以通过环境变量设置参数。 stackoverflow.com/questions/16144455/…
  • 不,不是,提供额外的选项
  • 看来我明白了。您想发送命令以从您的主机/站点开始测试到其他机器/站点并打印结果?
  • 如果我是对的,那就有想法。您可以在您的测试运行机器上运行服务器,并监听一些命令,例如 - 使用来自 body/url 的名称运行测试。然后,您可以生成子进程:child.exec("mocha", ["params"], ....,将子标准输出保存到变量并将其发送回主服务器。
  • 根据你的话“这应该使这个测试在输出中变成红色,因为result不是'成功'”expect(test.result).to.be.equal('success')?对不起,我真的很想帮忙,但不知道这是你的问题

标签: javascript node.js unit-testing mocha.js


【解决方案1】:

希望我正确理解了这些要求。我实现的是一个集成到 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的Runnablerun实现,运行传递的函数来获取测试结果,并设置ProxyTest接口中的必填字段与mocha测试兼容。

用法

在你的测试中使用proxyTest 全局注册一个新的摩卡测试

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 AND,这样您就不能使用像 describe 这样的 mocha BDD 词汇。如果您“扩展”(在这种情况下:复制一些代码)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;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-17
    • 1970-01-01
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 2014-05-01
    • 2017-02-03
    相关资源
    最近更新 更多