Как динамически генерировать тесты Mocha в блоке description () before ()? - PullRequest
0 голосов
/ 08 ноября 2018

Я создаю набор тестов mocha, который тестирует утилиту командной строки, которую вызывает наше приложение nwjs, которая принимает файлы и создает выходной файл json. У меня есть тысячи комбинаций входных файлов и мои тесты (и), которые я хочу сгенерировать, зависят от содержимого вывода json из утилиты cmdline.

Моча, похоже, хочет, чтобы я создал все предварительные версии it (), но это означает, что эти сценарии нужно запускать заранее и захватывать вывод json. Я надеялся сделать:

'use strict';
const path = require('path');
const glob = require('glob');
const expect = require('sharedjs/chai-wrapper').expect;
const utils = require('sharedjs/utils');

describe('Generated Tests:', function() {
  let testNum = 0;
  let globOpts = { nodir: true }
  let type1files = glob.sync(path.join(filetype1_dir, '*'), globOpts);
  let type2files = glob.sync(path.join(filetype2_dir, '*'), globOpts);
  for (let i = 0; i < type1files.length; i++) {
    for (let j = 0; j < type2files.length; j++) {
      testNum++;
      let testName = utils.mkTestName(testNum, i, j);

      describe(testName, function() {
        let run;
        before(function() {
          run = utils.runCommand(type1files[i], type2files[j]);
          // run = { status: result.status, command: result.args.join(' '), output: fse.readJsonSync(outfile) }
          if (run.status !== 0) {
            throw new Error(run.status+'='+run.command);
          }
        });

        for (let key in run.output.analysis) {
          it(key+'=0', function() {
            expect(run.output.analysis[key].value).to.be.equal('0', key+'=0');
          } 
        }
      });
    }
  }
});

Я буду делать тысячи вызовов командной строки здесь. Я не хочу делать их все заранее, кэшировать файлы (или, что еще хуже, загружать все объекты json в память), а затем запускать тесты.

Я знаю, что могу создать высокоуровневый тест «validate json», а затем просто выполнить там функцию wait (), но с этим есть две проблемы. Во-первых, они не будут независимыми именованными тестами, показанными как сбои, а во-вторых, первый ожидаемый сбой не сработает, поэтому у меня не будет видимости других ошибок дальше по json.

Идеи

- ОБНОВЛЕНО ОБРАЗЦОМ ВЫВОДА JSON ИЗ Utils.runCommand () -

{
    data1: { ... },
    data2: { ... },
    analysis: {
        dynamicKey1: <analysisObj>,
        dynamicKey...: <analysisObj>,
        dynamicKeyN: <analysisObj>
    }
}

Ключи в анализе зависят от типа вводимых данных, и существует большое количество возможностей. Имя динамических клавиш может меняться от запуска к запуску. С точки зрения тестирования, меня не интересует название ключа, а то, что он analysisObj соответствует. Например, если я передаю идентичные data1 и data2 в utils.runCommand (), то часть analysisObj, представляющая дельту между ними, должна быть равна нулю по всем направлениям.

Я не получаю analysisObjs до тех пор, пока я не выполню скрипт, и если я запускаю 100 000 тестов, мне не нужно предварительно запускать или предварительно загружать все это в память или файловую систему.

1 Ответ

0 голосов
/ 09 ноября 2018

Я хочу поблагодарить @JoshLee за указание на несколько полезных путей исследования.

После просмотра кода мокко, сосредоточив внимание в основном на:

Я узнал, что

  1. вызов description () возвращает объект Suite
  2. Объект Suite содержит тесты для запуска (suite.tests)
  3. Когда запускается пакет before (), тесты не смотрятся на
  4. Я могу добавить столько тестов, сколько захочу в методе before (), используя suite.addTest (), и все они будут запущены
  5. Лучше всего то, что мой utils.runCommand () запускается только в начале каждого набора тестов, а каждый набор тестов запускается последовательно. (ранее мои добавленные тесты выполнялись после того, как все начальные блоки описаний были выполнены один раз)

Выходные данные соответствуют ожидаемым, и результаты отражают правильное количество тестов. Я запустил эту автоматическую генерацию чуть более 50 000 тестов, неравномерно распределенных по 1980 комплектам тестов, используя для репортера mochawesome , и он отлично работал.

Для этого необходимо выполнить 5 шагов, описанных в обновленном фрагменте кода ниже.


'use strict';
const path = require('path');
const glob = require('glob');
const expect = require('sharedjs/chai-wrapper').expect;
const utils = require('sharedjs/utils');

// Step 1: Pull in Test class directly from mocha
const Test = require('mocha/lib/test');

// Step 2: Simulates it() from mocha/lib/interfaces/bdd.js
//   I ignore the isPending() check from bdd.js. I don't know
//   if ignoring it is required, but I didn't see a need to add
//   it for my case to work
function addTest(suite, title, fn) {
  let test = new Test(title, fn);
  test.file = __filename;
  suite.addTest(test);
  return test;
}

let testNum = 0;
let globOpts = { nodir: true }
let type1files = glob.sync(path.join(filetype1_dir, '*'), globOpts);
let type2files = glob.sync(path.join(filetype2_dir, '*'), globOpts);
for (let i = 0; i < type1files.length; i++) {
  for (let j = 0; j < type2files.length; j++) {
    testNum++;
    let testName = utils.mkTestName(testNum, i, j);

    // Step 3: Save the suite object so that we can add tests to it.
    let suite = describe(testName, function() {
      let run;
      before(function() {
        run = utils.runCommand(type1files[i], type2files[j]);
        // run = { status: result.status, command: result.args.join(' '),
        //         output: fse.readJsonSync(outfile) }
        if (run.status !== 0) {
          throw new Error(run.status+'='+run.command);
        }

        for (let key in run.output.analysis) {
          // Step 4: Dynamically add tests 
          //   suite is defined at this point since before() is always
          //   run after describe() returns.
          addTest(suite, key+'=0', function() {
            expect(run.output.analysis[key].value).to.be.equal('0', key+'=0');
          });
        }            
      });
    });

    // Step 5: Add dummy test in describe() block so that it will be run.
    //   Can be it() for a pass result or it.skip() for pending.
    it('Placeholder for ' + testName, function () {
      expect(true).to.be.true;
    });
  }
}
...