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'

Можно ли установить результат теста и продолжительность без "действительно" его запуска? И получить весь визуальный вывод мокко на терминал?

edit: Этот вопрос не о том, как передавать переменные с результатами теста, образуя дочерний процесс в основной процесс. он уже работает для меня.

Ответы

Ответ 1

Надеюсь, я правильно понял требования. То, что я реализовал, - это форматир результата теста для мокки, который интегрирован в mocha.

Для интеграции с mocha эта реализация описывает пользовательский интерфейс mocha для прокси-результатов теста из тестов, выполненных в другой среде.

Чтобы использовать этот интерфейс, аргумент -u должен быть передан в mocha при запуске 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 для совместимости с мокко-тестами.

Использование

В ваших тестах используйте proxyTest global для регистрации нового теста с помощью 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 И, что вы не можете использовать словарный запас Mocha BDD, например describe. Второй недостаток можно устранить, если вы "растянете" (в данном случае: скопируйте некоторый код) интерфейс 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;
};