приєднання тестів з декількох файлів за допомогою 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


1
Цікаво, чому тести потрібно об’єднувати в один файл?
thgaskell

2
Для обміну місцевими змінними та організації
coiso

Це може мати більше сенсу, якщо ви включите тести до питання. Схоже, ви можете схилятися до інтеграційних тестів (на відміну від модульних тестів). Як правило, вам не потрібно обмінюватися змінними між тестами.
thgaskell

2
І велика проблема полягає в тому, що я волів би мати приблизно 20 файлів, ніж 1 файл
huuuuge

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з'являються вони всередині describeв 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що малоймовірно. Це триває лише на час випробувань. Мені ще не нашкодило, але я повідомлю вас, як тільки це мене
вкусить

Яка різниця між тим, як importTestтелефонувати, require('path')()наприклад?
CherryNerd

@CreasolDev importTestФункція - це просто функція зручності. Найголовніше, що він робить, - це обгортання requireдзвінка в describeблок. Важливо, щоб requireвиклик був завернутий, describeінакше модулі не будуть ізольовані у своєму власному блоці, а будь-який гачок, встановлений імпортованим файлом, буде встановлений на неправильному блоці. Якби importTestйого замінили прямим викликом requireбез обтікання describe, то модулі a/aі b/bподіляли б хуки. Наприклад, beforeEachгачок, встановлений b/b, також запускається перед кожним тестом в a/a.
Луїс

1
Я б НЕ запускав будь-яку логіку, подібну раніше, Кожен у своєму верхньому рівні описує. Нехай кожен файл робить своє перед кожним "матеріалом". Якщо ви зробите це, ви будете поєднувати свої тести між собою та не пов'язані з ними.
PositiveGuy

1
Я б також зробив обтікання описів у їхні відповідні файли, а не у функції importTest. Описання верхнього рівня у кожному відповідному файлі в будь-якому випадку повинні описувати цілі їх тестових наборів
PositiveGuy

35

Незважаючи на те, що це не може бути безпосередньо пов'язано з питанням, відповідь, яку я шукав, була:

$ mocha --recursive

Буде виконувати всі тести в підкаталогах папки "test". Охайний. Заощаджує необхідність вести список тестів, які я хочу завантажити, і насправді просто завжди виконувати все.


3
Краща відповідь! Набагато простіші за інші запропоновані рішення.
caiosm1005

12
@ caiosm1005 Ця відповідь насправді не вирішує проблему, представлену ОП . Звичайно, якщо вам не потрібно робити те, що хоче зробити операційна програма , ви повинні використовувати це. Однак, якщо ви хочете обернути кожен тестовий файл у декілька describeблоків, describeблоки, що охоплюють файли, --recursiveцього робити не будуть. Вважаючи, що це не вирішує проблему ОП, я б не називав це "найкращим".
Луї

@louis - Я вважаю, що ти можеш обернути кожен окремий файл у describeблоки
Ян Джеймісон

4
@IanJamieson ОП намагається мати декілька файлів, охоплених одним describe блоком. Подивіться на питання. Блок "Контролери" describeповинен охоплювати тести ./controllertests/messages.jsта ./controllertests/users.js. Шльопання --recursiveна виклик Мокки не створює чарівного describe("Controllers"блоку.
Луїс

3
@Louis Просто намагаюся допомогти. Вибачте, якщо я образив вас, намагаючись магічно створювати 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');

3
Хтось не заперечив би додавання коду для файлів у каталоги контролерів та моделей? Було б чудово мати повний приклад.
Гевін

@Gavin - це будуть просто тестові костюми, щоб вони містилиdescribe('mytest', function() { /* ..... etc */ });
Ян Джеймісон

8

Я знаю, що це стара публікація, але я хотів поспішатись із тим, що було для мене хорошим рішенням, дуже подібним до методу, запропонованого ОП.

Проект, над яким я працюю, добре перевірений, і тести продовжують зростати. У підсумку я використав, 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
Найкраще додати опис разом із кодом, щоб інші могли визначити, чи є це прийнятною відповіддю.
Сувер

2
Чому петля? Що входить ./Test.js? Хто знає? Для протоколу, на даний момент я є головним відповідачем у тезі mocha . Я знаю Мокку всередині та зовні, але не можу зрозуміти цю відповідь.
Луїс

@Louis здається, що він хотів запустити тести n разів, використовуючи цикл.
Akash Agarwal
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.