объединение тестов из нескольких файлов с помощью mocha.js

87

Я пытаюсь объединить все тесты из нескольких файлов в один файл, примерно так:

  describe('Controllers', function() {
    describe('messages.js', function() {
      require('./controllertests/messages').test(options);
    })
    describe('users.js', function() {
      require('./controllertests/users').test(options);
    })
  })

Я почти уверен, что это не лучший способ присоединиться к тестам, мне сложно найти примеры того, как это сделать: s

Coiso
источник
1
Любопытно, а зачем нужно объединять тесты в один файл?
thgaskell
2
За совместное использование локальных переменных и организации
coiso
Будет больше смысла, если вы включите тесты в вопрос. Похоже, вы склоняетесь к интеграционным тестам (в отличие от модульных тестов). Как правило, вам не нужно разделять переменные между тестами.
thgaskell
2
И большая проблема в том, что я бы предпочел иметь 20 файлов, а не 1 huuuuge
coiso
2
Кроме того, если вы посмотрите, как Mocha обрабатывает наборы с концепцией, .only()было бы полезно иметь возможность describe.only()запускать целый каталог тестов. Вот что привело меня сюда.
Крис

Ответы:

113

Если вы хотите включить несколько модулей в свою describeиерархию, как вы это делаете в своем вопросе, то, что вы делаете, в значительной степени это , если вы не хотите написать собственный тестовый загрузчик для Mocha. Написание собственного загрузчика не будет проще и не сделает ваш код более понятным, чем тот, который у вас уже есть.

Вот пример того, как я бы изменил несколько вещей. testПодкаталог в этом примере организован следующим образом:

.
└── test
    ├── a
    │   └── a.js
    ├── b
    │   └── b.js
    ├── common.js
    └── top.js

top.js:

function importTest(name, path) {
    describe(name, function () {
        require(path);
    });
}

var common = require("./common");

describe("top", function () {
    beforeEach(function () {
       console.log("running something before each test");
    });
    importTest("a", './a/a');
    importTest("b", './b/b');
    after(function () {
        console.log("after all tests");
    });
});

importTestФункция просто чтобы показать , как можно было бы обрабатывать повторение импорта нескольких модулей без необходимости перепечатывать в целом describe(... require...вещь каждый раз. commonМодуль предназначен для хранения , что вам нужно использовать в нескольких модулях тестового набора. На самом деле я его не использую, topно при необходимости его можно использовать там.

Я отмечу здесь, что он beforeEachбудет запускать свой код перед каждым тестом, зарегистрированным в itзависимости от того, появляются ли они внутри describein topили они появляются в любом из импортированных модулей . При --recursiveэтом beforeEachкод нужно было бы скопировать в каждый модуль или, возможно, у вас был бы beforeEachкрючок в каждом модуле, который вызывает функцию, импортированную из общего модуля.

Кроме того, afterловушка запускается после всех тестов в наборе. Это не может быть воспроизведено с помощью --recursive. Если вы используете --recursiveи добавляете код afterдля каждого модуля, он будет выполняться один раз для каждого модуля, а не один раз для всего теста.

Отображение всех тестов под одним topзаголовком невозможно воспроизвести с помощью --recursive. С --recursiveкаждым файлом можно было бы иметь, describe("top"но это создало бы новый topзаголовок для каждого файла.

common.js:

var chai = require("chai");

var options = {
    foo: "foo"
};

exports.options = options;
exports.chai = chai;
exports.assert = chai.assert;

Использование модуля с таким названием - commonэто то, что я делал в некоторых своих тестовых наборах, чтобы избежать необходимости requireповторять кучу вещей снова и снова и хранить глобальные переменные только для чтения или функции, которые не сохраняют состояние. Я предпочитаю не загрязнять globalобъект, как в ответе thgaskell, потому что этот объект действительно глобален и доступен даже в сторонних библиотеках, которые может загружать ваш код. Я не считаю это приемлемым в моем коде.

a/a.js:

var common = require("../common");
var options = common.options;
var assert = common.assert;

it("blah a", function () {
    console.log(options.foo);
    assert.isTrue(false);
});

b/b.js:

it("blah b", function () {});
Луи
источник
3
Хотя я согласен с тем, что вы не должны загрязнять globalобласть видимости, я использую это для библиотек утверждений, чтобы тестовые файлы были более чистыми. Вы же не перезаписываете global.process. Локальные переменные будут иметь приоритет, globalесли другие библиотеки явно не вызывают, global.XYZчто маловероятно. Он действует только на время проведения тестов. Еще не причинил мне вреда, но я дам вам знать, как только он укусит меня за задницу :)
thgaskell
В чем разница между importTestзвонком и require('path')()например?
CherryNerd 01
@CreasolDev importTestФункция просто удобная. Важная вещь, которую он делает, - это перенос requireвызова в describeблок. Важно, чтобы requireвызов был заключен в оболочку, describeиначе модули не будут изолированы в своем собственном блоке, и любой обработчик, установленный импортированным файлом, будет установлен на неправильный блок. Если бы importTestбыло заменено прямым вызовом requireбез упаковки describe, то модули a/aи были b/bбы совместно использовать хуки. Например, установленный beforeEachхук b/bтакже будет запускаться перед каждым тестом в a/a.
Луи
1
Я бы НЕ запускал никакой логики, такой как beforeEach в вашем верхнем уровне description. Пусть каждый файл делает свое дело перед каждым "материалом". Если вы это сделаете, вы будете связывать свои тесты друг с другом и несвязанной реализацией.
PositiveGuy
1
Я бы также поместил описания в соответствующие файлы, а не в функцию importTest.
PositiveGuy
35

Хотя это может не быть напрямую связано с вопросом, я искал ответ:

$ mocha --recursive

Выполнит все тесты в подкаталогах папки "test". Аккуратно. Избавляет от необходимости вести список тестов, которые я хочу загрузить, и на самом деле просто всегда все запускать.

Ян Джеймисон
источник
3
Лучший ответ! Намного проще других предлагаемых решений.
caiosm1005
12
@ caiosm1005 Этот ответ на самом деле не решает проблему, представленную OP . Конечно, если вам не нужно делать то, что хочет сделать OP , вам следует использовать это. Однако, если вы хотите разделить каждый тестовый файл на несколько describeблоков, describeблоки, охватывающие файлы, --recursiveэтого не сделают. Поскольку это не решает проблему OP, я бы не назвал его «лучшим».
Луи
@louis - Я считаю, что вы можете обернуть каждый отдельный файл describeблоками
Ян Джеймисон
4
@IanJamieson OP пытается охватить несколько файлов одним describe блоком. Посмотри на вопрос. Блок «Контроллеры» describeдолжен включать в себя тесты ./controllertests/messages.jsи ./controllertests/users.js. Прикосновение --recursiveк вызову Mocha не создает магического describe("Controllers"блока.
Луи
3
@ Луис Просто пытаюсь помочь. Извините, если я обидел вас, пытаясь волшебным образом создавать describeблоки - чему я научился делать у самого Дамблдора.
Ян Джеймисон
16

Нет ничего, что мешало бы вам запустить несколько тестовых файлов. Как правило, каждый тест не должен зависеть от результатов другого теста, поэтому совместное использование переменных - это не то, что вам нужно.

Вот пример того, как вы можете организовать свои тестовые файлы.

.
├── app.js
└── test
    ├── common.js
    ├── mocha.opts
    │
    ├── controllers
    │   ├── messages-controller.js
    │   └── users-controller.js
    │
    └── models
        ├── messages-model.js
        └── users-model.js

Затем внутри вашего mocha.optsфайла обязательно установите этот --recursiveпараметр.

mocha.opts

--ui bdd
--recursive

Если есть общие модули , которые вы хотите включить все файлы, вы можете добавить , что в common.jsфайл. Файлы в корне testкаталога будут запускаться перед файлами во вложенных каталогах.

common.js

global.chai = require('chai');
global.assert = chai.assert;
global.expect = chai.expect;
chai.should();
chai.config.includeStack = true;

process.env.NODE_ENV = 'test';

// Include common modules from your application that will be used among multiple test suites.
global.myModule = require('../app/myModule');
Thgaskell
источник
3
Кто-нибудь не возражает добавить код для файлов в каталогах контроллеров и моделей? Было бы здорово получить полный пример.
Гэвин
@Gavin - это будут просто тестовые костюмы, чтобы они содержалиdescribe('mytest', function() { /* ..... etc */ });
Ян Джеймисон,
8

Я знаю, что это старый пост, но я хотел присоединиться к тому, что было для меня хорошим решением, очень похожим на метод, предложенный OP.

Проект, над которым я работаю, хорошо протестирован, и количество тестов продолжает расти. В итоге я использовал, requireпотому что он синхронный и, следовательно, немного упрощает составление ваших тестов без особых изменений в архитектуре:

// inside test/index.js

describe('V1 ROUTES', () => {
  require('./controllers/claims.test');
  require('./controllers/claimDocuments.test');
  require('./controllers/claimPhotos.test');
  require('./controllers/inspections.test');
  require('./controllers/inspectionPhotos.test');
  require('./controllers/versions.test');
  require('./services/login.v1.test');
});

describe('V2 ROUTES', () => {
  require('./services/login.v2.test');
  require('./services/dec-image.v2.test');
});

describe('V3 ROUTES', () => {
  require('./services/login.v3.test');
  require('./services/getInspectionPhotosv3.test');
  require('./services/getPolicyInfo.v3.test');
});

describe('ACTIONS', () => {
  require('./actions/notifications.test');
});
Майк Флеминг
источник
2

У меня была аналогичная проблема, когда у меня было несколько тестов для классов в одной категории, и я хотел сгруппировать их вместе, чтобы упростить их просмотр в среде IDE. Все мои тесты и код уже использовали модули ES6 - я не хотел их все переписывать, чтобы использоватьrequire как я видел в других примерах.

Я решил это, describeэкспортировав мою «группировку» , а затем импортировав ее в мои тестовые файлы и программно добавив их к импортированным describe. В итоге я создал вспомогательный метод, чтобы абстрагироваться от всей сантехники.

В someCategory.spec.js

const someCategory= describe("someCategory", () => {});


// Use this just like a regular `describe` to create a child of this scope in another file
export default function describeMember(skillName, testFn) {
  return describe(skillName, function configureContext() {
    // Make context a child of `someCategory` context
    function Context() {}
    Context.prototype = someCategory.ctx;
    this.ctx = new Context();
    // Re-parent the suite created by `describe` above (defaults to root scope of file it was created in)
    this.parent.suites.pop();
    someCategory.addSuite(this);
    // Invoke the fn now that we've properly set up the parent/context
    testFn.call(this);
  });
}

В индивидуальных тестах:

import { default as describeCategoryMember } from './someCategory.spec';

describeCategoryMember('something', () => {
    describe('somethingElse', () => {
        ...
    });

    it('a test', () => {
        ...
    });
})
Йон Сенчина
источник
-11
describe( 'Running automation test, Please wait for all test to complete!'.red, function () {


    var run = require( './Test.js' );

    for ( var i = 0; i < 2; i++ ) {
        run.badLogin();
        run.loginLimited();
        run.acceptJob();
        run.drivingToJob();
        run.arrivedAtJob();
        run.towingJob();
        run.arrivedDestination();
        run.jobComplete();
        run.restrictionLicensePlate();
        run.newNodeMainMenu();
        run.newNodeMainMenuToDrafts();
        run.draftDelete();
        run.resetAllData();
        run.companyVehicle();
        run.actionsScreenClockInOut();
        run.mainMenuLogout();
        run.loginAdmin();
        run.actionsScreenLogout();
    }
} );
Майк
источник
3
Лучше всего добавить описание вместе с кодом, чтобы другие могли определить, является ли это приемлемым ответом.
Suever 01
2
Почему петля? Что внутри ./Test.js? Кто знает? Для справки, в настоящее время я лучший ответчик по тегу мокко . Я знаю Мокко от и до, но я не могу понять этот ответ.
Луи,
@Louis похоже, что он хотел запустить тесты n раз, используя цикл.
Акаш Агарвал