łączenie testów z wielu plików z mocha.js

87

Próbuję połączyć wszystkie testy z wielu plików w jednym pliku, coś takiego:

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

Jestem prawie pewien, że nie jest to najlepszy sposób dołączania do testów, mam pewne trudności ze znalezieniem przykładów, jak to zrobić: s

coiso
źródło
1
Ciekawe, dlaczego testy muszą być połączone w jeden plik?
thgaskell
2
Za udostępnianie zmiennych lokalnych i organizacji
coiso,
Bardziej sensowne może być uwzględnienie testów w pytaniu. Wygląda na to, że skłaniasz się ku testom integracyjnym (w przeciwieństwie do testów jednostkowych). Ogólnie nie ma potrzeby udostępniania zmiennych między testami.
thgaskell
2
I duży problem polega na tym, że wolałbym mieć około 20 plików niż 1 plik huuuuge
coiso
2
Ponadto, jeśli spojrzysz na to, jak Mocha radzi sobie z pakietami z koncepcją tego .only(), przydatna może być możliwość describe.only()uruchomienia całego katalogu testów. To właśnie mnie tu sprowadziło.
Chris

Odpowiedzi:

113

Jeśli chcesz dołączyć wiele modułów do swojej describehierarchii, tak jak robisz to w swoim pytaniu, to, co robisz, jest prawie tym , chyba że chcesz napisać niestandardowy program ładujący test dla Mocha. Napisanie niestandardowego modułu ładującego nie byłoby łatwiejsze ani nie uczyniłoby kodu bardziej przejrzystym niż to, co już masz.

Oto przykład, jak zmieniłbym kilka rzeczy. testPodkatalogu w tym przykładzie jest organizowane jako:

.
└── 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");
    });
});

importTestFunkcja jest po prostu pokazać, jak to będzie możliwe, aby poradzić sobie z powtórzeniem importowania wielu modułów bez konieczności ponownego wpisywania cały describe(... require...rzeczą każdym razem. commonModuł ten służy do przechowywania, czego potrzebujesz do korzystania z wielu modułów zestawu testowego. W rzeczywistości nie używam go, topale w razie potrzeby można go tam użyć.

W tym miejscu zauważę, że program beforeEachuruchomi swój kod przed każdym zarejestrowanym testem z informacją, itczy pojawiają się one w programie describein, topczy też pojawiają się w którymkolwiek z zaimportowanych modułów . Dzięki --recursivetemu beforeEachkod musiałby zostać skopiowany do każdego modułu lub być może w każdym module byłby punkt beforeEachzaczepienia, który wywołuje funkcję zaimportowaną ze wspólnego modułu.

Ponadto afterhak zostanie uruchomiony po wszystkich testach w pakiecie. Nie można tego powtórzyć za pomocą --recursive. Jeśli użyjesz --recursivei dodasz kod afterdo każdego modułu, będzie on wykonywany raz na moduł, a nie tylko raz na cały test.

Wyświetlenia wszystkich testów pod pojedynczym topnagłówkiem nie można powielić za pomocą --recursive. Z --recursivekażdego pliku może mieć describe("top", ale to byłoby utworzyć nowy topnagłówek dla każdego pliku.

common.js:

var chai = require("chai");

var options = {
    foo: "foo"
};

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

Używanie modułu o commontakiej nazwie jest czymś, co zrobiłem w niektórych moich zestawach testowych, aby uniknąć konieczności ciągłego requirewykonywania wielu rzeczy i utrzymywania globalnych zmiennych tylko do odczytu lub funkcji, które nie zachowują stanu. Wolę nie zanieczyszczać globalobiektu, jak w odpowiedzi thgaskell, ponieważ ten obiekt jest naprawdę globalny i dostępny nawet w bibliotekach stron trzecich, który może ładować twój kod. To nie jest coś, co uważam za akceptowalne w moim kodzie.

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 () {});
Louis
źródło
3
Chociaż zgadzam się, że nie należy zanieczyszczać globalzakresu, używam tego w bibliotekach asercji, aby zachować czystość plików testowych. To nie tak, że nadpisujesz global.process. Zmienne lokalne będą nadpisywać, globalchyba że inne biblioteki będą jawnie wywoływać, global.XYZco jest mało prawdopodobne. Trwa tylko przez czas trwania testów. Jeszcze mnie nie zranił, ale dam ci znać, kiedy ugryzie mnie w tyłek :)
thgaskell
Jaka jest różnica między importTesti require('path')()na przykład dzwonieniem ?
CherryNerd
@CreasolDev Ta importTestfunkcja jest tylko funkcją zwiększającą komfort. Ważną rzeczą, jaką robi, jest zawijanie requirewywołania do describebloku. Ważne jest, aby requirewywołanie zostało zawinięte, w describeprzeciwnym razie moduły nie zostaną odizolowane w swoim własnym bloku, a wszelkie zaczepy ustawione przez importowany plik zostaną ustawione na niewłaściwy blok. Gdyby importTestzostał zastąpiony bezpośrednim wywołaniem requirebez zawijania describe, wówczas moduły a/ai b/bwspółdzieliłyby punkty zaczepienia. Na przykład, beforeEachhak ustawiony w b/bbędzie również uruchamiany przed każdym testem a/a.
Louis
1
NIE uruchomiłbym żadnej logiki, takiej jak wcześniej.Każdy opis na najwyższym poziomie. Niech każdy plik zrobi swoje przed każdym „rzecz”. Jeśli to zrobisz, połączysz testy ze sobą i niepowiązaną implementacją.
PositiveGuy,
1
Zrobiłbym również zawijanie opisów w odpowiednich plikach, a nie w funkcji importTest. Opisy najwyższego poziomu w każdym odpowiednim pliku powinny i tak opisywać przeznaczenie ich zestawów testów
PositiveGuy
35

Chociaż może to nie być bezpośrednio związane z pytaniem, odpowiedź, której szukałem, brzmiała:

$ mocha --recursive

Wykonuje wszystkie testy w podkatalogach folderu „test”. Schludny. Oszczędza konieczności utrzymywania listy testów, które chcę załadować i właściwie zawsze wszystko uruchamiam.

Ian Jamieson
źródło
3
Najlepsza odpowiedź! Znacznie prostsze niż inne proponowane rozwiązania.
caiosm1005
12
@ caiosm1005 Ta odpowiedź w rzeczywistości nie rozwiązuje problemu przedstawionego przez PO . Jasne, jeśli nie musisz robić tego, co chce zrobić OP , powinieneś tego użyć. Jeśli jednak chcesz owinąć każdy plik testowy w wiele describebloków, describebloki obejmujące pliki, --recursivenie zrobią tego. Ponieważ nie rozwiązuje to problemu PO, nie nazwałbym tego „najlepszym”.
Louis
@louis - Uważam, że każdy oddzielny plik można opakować w describebloki
Ian Jamieson
4
@IanJamieson OP próbuje objąć wiele plików jednym describe blokiem. Spójrz na pytanie. Blok „Kontrolery” describepowinien obejmować testy ./controllertests/messages.jsi ./controllertests/users.js. Uderzenie --recursivew inwokację Mocha nie tworzy magicznie describe("Controllers"bloku.
Louis
3
@Louis Próbuję tylko pomóc. Przepraszam, jeśli cię obraziłem, próbując magicznie tworzyć describebloki - czego nauczyłem się od samego Dumbledore'a.
Ian Jamieson
16

Nic nie stoi na przeszkodzie, aby uruchomić wiele plików testowych. Ogólnie rzecz biorąc, każdy test nie powinien być zależny od wyników innego testu, więc udostępnianie zmiennych nie jest czymś, co chciałbyś robić.

Oto przykład, jak możesz zorganizować swoje pliki testowe.

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

Następnie w mocha.optspliku upewnij się, że ustawiłeś --recursiveopcję.

mocha.opts

--ui bdd
--recursive

Jeśli nie wspólne moduły, które chcesz umieścić na wszystkich plikach, które można dodać do common.jspliku. Pliki w katalogu głównym testbędą uruchamiane przed plikami w zagnieżdżonych katalogach.

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
źródło
3
Czy ktoś miałby coś przeciwko dodaniu kodu dla plików w katalogach kontrolerów i modeli? Byłoby wspaniale mieć pełny przykład.
Gavin
@Gavin - to będą tylko kombinezony testowe, więc będą zawieraćdescribe('mytest', function() { /* ..... etc */ });
Ian Jamieson
8

Wiem, że to stary post, ale chciałem się przyłączyć do tego, co było dla mnie dobrym rozwiązaniem, bardzo podobnym do metody zaproponowanej przez OP.

Projekt, nad którym pracuję, jest dobrze przetestowany, a testy wciąż się rozwijają. Skończyło się na tym, requireże używam, ponieważ jest synchroniczny i dlatego nieco ułatwia komponowanie testów bez zbytniej zmiany architektury:

// 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');
});
Mike Fleming
źródło
2

Miałem podobny problem, gdy miałem kilka testów dla klas z tej samej kategorii i chciałem je zgrupować, aby ułatwić przeglądanie ich w IDE. Wszystkie moje testy i kod korzystały już z modułów ES6 - nie chciałem przepisać ich wszystkich requiretak, jak widziałem w innych przykładach.

Rozwiązałem to describe, wyeksportowałem moje „grupowanie” , a następnie zaimportowałem je do plików testowych i programowo dodałem do zaimportowanych describe. Skończyło się na stworzeniu metody pomocniczej, aby odciąć całą hydraulikę.

W 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);
  });
}

W poszczególnych testach:

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

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

    it('a test', () => {
        ...
    });
})
Jon Senchyna
źródło
-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();
    }
} );
Mikrofon
źródło
3
Najlepiej jest dodać opis wraz z kodem, aby inni mogli określić, czy jest to akceptowalna odpowiedź.
Suever
2
Dlaczego pętla? Co jest w ./Test.jsśrodku? Kto wie? Dla przypomnienia, obecnie jestem pierwszą osobą odpowiadającą w kategorii mokka . Znam Mokkę od podszewki, ale nie rozumiem tej odpowiedzi.
Louis
@Louis wydaje się, że chciał przeprowadzić testy n razy, używając pętli.
Akash Agarwal