Verbinden von Tests aus mehreren Dateien mit mocha.js

87

Ich versuche, alle Tests aus mehreren Dateien in einer Datei zusammenzuführen.

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

Ich bin mir ziemlich sicher, dass dies nicht der beste Weg ist, um an Tests teilzunehmen. Ich habe einige Schwierigkeiten, Beispiele dafür zu finden: s

coiso
quelle
1
Neugierig, warum müssen die Tests in einer Datei zusammengefasst werden?
Thgaskell
2
Für den Austausch lokaler Variablen und Organisation
Coiso
Es könnte sinnvoller sein, wenn Sie die Tests in die Frage aufnehmen. Es hört sich so an, als würden Sie sich eher Integrationstests zuwenden (im Gegensatz zu Unit-Tests). Im Allgemeinen sollten Sie keine Variablen für Tests freigeben müssen.
Thgaskell
2
Und das große Problem ist, dass ich lieber 20 Dateien als 1 riesige Datei haben möchte
Coiso
2
Wenn Sie sich ansehen, wie Mocha mit Suiten .only()umgeht, kann es hilfreich sein, describe.only()ein ganzes Verzeichnis von Tests ausführen zu können . Das hat mich hierher gebracht.
Chris

Antworten:

111

Wenn Sie mehrere Module in Ihre describeHierarchie aufnehmen möchten, wie Sie es in Ihrer Frage tun, tun Sie dies so ziemlich, es sei denn, Sie möchten einen benutzerdefinierten Testlader für Mocha schreiben. Das Schreiben des benutzerdefinierten Loaders wäre nicht einfacher oder macht Ihren Code klarer als das, was Sie bereits haben.

Hier ist ein Beispiel, wie ich ein paar Dinge ändern würde. Das testUnterverzeichnis in diesem Beispiel ist wie folgt organisiert:

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

Die importTestFunktion soll nur zeigen, wie es möglich wäre, die Wiederholung des Imports mehrerer Module zu handhaben, ohne das describe(... require...Ganze jedes Mal neu eingeben zu müssen . Das commonModul soll enthalten, was Sie in mehreren Modulen der Testsuite verwenden müssen. Ich benutze es nicht wirklich intop aber es könnte dort verwendet werden, wenn nötig.

Ich werde hier bemerken, dass der beforeEachCode vor jedem einzelnen Test ausgeführt wird, der registriert wird, itob er im describeIn topoder in einem der importierten Module erscheint . Mit müsste --recursiveder beforeEachCode in jedes Modul kopiert werden, oder Sie hätten beforeEachin jedem Modul einen Hook, der eine aus einem gemeinsamen Modul importierte Funktion aufruft.

Außerdem wird der afterHook nach allen Tests in der Suite ausgeführt. Dies kann nicht mit repliziert werden --recursive. Wenn Sie --recursiveden Code afterjedes Moduls verwenden und hinzufügen , wird er einmal pro Modul und nicht nur einmal für das gesamte Modul ausgeführt Test ausgeführt.

Wenn alle Tests unter einer einzigen topÜberschrift angezeigt werden, kann dies nicht mit repliziert werden --recursive. Mit --recursivejeder Datei könnte describe("top"aber dies würde eine neue topÜberschrift für jede Datei erstellen.

common.js::

var chai = require("chai");

var options = {
    foo: "foo"
};

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

Die Verwendung eines Moduls mit commondiesem Namen habe ich in einigen meiner Testsuiten durchgeführt, um zu vermeiden, dass immer wieder requireeine Menge Dinge erledigt werden müssen, und um globale schreibgeschützte Variablen oder Funktionen zu speichern, die den Status nicht beibehalten. Ich ziehe es vor, das nicht zu verschmutzenglobal Objekt nicht wie in der Antwort von thgaskell , da dieses Objekt wirklich global ist und auch in Bibliotheken von Drittanbietern zugänglich ist, in die Ihr Code möglicherweise geladen wird. Dies ist in meinem Code nicht akzeptabel.

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
quelle
3
Ich bin damit einverstanden, dass Sie den globalUmfang nicht verschmutzen sollten , aber ich verwende dies für die Assertionsbibliotheken, um die Testdateien sauberer zu halten. Es ist nicht so, als würdest du überschreiben global.process. Lokale Variablen werden überschrieben, es globalsei denn, andere Bibliotheken rufen explizit auf, global.XYZwas unwahrscheinlich ist. Es dauert nur für die Dauer der Tests. Hat mich noch nicht verletzt, aber ich werde dich wissen lassen, sobald es mich in den Arsch beißt :)
Thgaskell
Was ist der Unterschied zwischen importTestund Anruf require('path')()zum Beispiel?
CherryNerd
@CreasolDev Die importTestFunktion ist nur eine Komfortfunktion. Das Wichtigste dabei ist, den requireAufruf in einen describeBlock zu packen. Es ist wichtig, dass der requireAufruf eingeschlossen wird, da describesonst die Module nicht in einem eigenen Block isoliert werden und alle von der importierten Datei gesetzten Hooks auf den falschen Block gesetzt werden. Wenn importTestdurch einen direkten Aufruf requireohne Umbruch ersetzt würde describe, würden sich die Module a/aund b/bHooks teilen. Zum Beispiel würde vor jedem Test ein beforeEachHook- b/bIn ausgeführt a/a.
Louis
1
Ich würde KEINE Logik wie zuvor ausführen, die in Ihrer obersten Ebene beschrieben wird. Lassen Sie jede Datei ihre eigenen vor jedem "Zeug" machen. In diesem Fall koppeln Sie Ihre Tests miteinander und mit der Implementierung ohne Bezug.
PositiveGuy
1
Ich würde auch das Umschließen von Beschreibungen in ihren jeweiligen Dateien durchführen, nicht in der importTest-Funktion. Die oberste Ebene, die in der jeweiligen Datei beschrieben wird, sollte ohnehin den Zweck ihrer Testsuiten beschreiben
PositiveGuy
33

Obwohl dies möglicherweise nicht direkt mit der Frage zusammenhängt, war die Antwort, nach der ich gesucht habe:

$ mocha --recursive

Führt alle Tests in Unterverzeichnissen des Ordners "test" aus. Ordentlich. Spart die Notwendigkeit, eine Liste von Tests zu führen, die ich laden möchte, und eigentlich immer alles auszuführen.

Ian Jamieson
quelle
3
Beste Antwort! Viel einfacher als andere vorgeschlagene Lösungen.
Caiosm1005
11
@ caiosm1005 Diese Antwort löst das vom OP präsentierte Problem nicht wirklich . Sicher, wenn Sie nicht tun müssen, was das OP tun möchte , sollten Sie dies verwenden. Wenn Sie jedoch jede Testdatei in mehrere describeBlöcke einschließen möchten , werden describeBlöcke, die sich über mehrere Dateien erstrecken, dies --recursivenicht tun. Da es das Problem des OP nicht löst, würde ich es nicht als "am besten" bezeichnen.
Louis
@ Louis - Ich glaube, Sie können jede einzelne Datei in describeBlöcke verpacken
Ian Jamieson
4
@IanJamieson Das OP versucht, mehrere Dateien von einem einzigen describe Block abzudecken . Schau dir die Frage an. Der describeBlock "Controller" sollte die Tests von ./controllertests/messages.jsund umfassen ./controllertests/users.js. Wenn Sie --recursiveauf einen Mokka-Aufruf klatschen, wird auf magische Weise kein describe("Controllers"Block erstellt.
Louis
3
@ Louis Ich versuche nur zu helfen. Es tut mir leid, wenn ich dich beleidigt habe, indem ich versucht habe, auf magische Weise describeBlöcke zu erstellen - was ich tatsächlich von Dumbledore selbst gelernt habe.
Ian Jamieson
15

Nichts hindert Sie daran, mehrere Testdateien auszuführen. Im Allgemeinen sollte jeder Test nicht von den Ergebnissen eines anderen Tests abhängig sein, sodass Sie keine Variablen gemeinsam nutzen möchten.

Hier ist ein Beispiel, wie Sie Ihre Testdateien organisieren können.

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

Stellen Sie dann in Ihrer mocha.optsDatei sicher, dass Sie die --recursiveOption festlegen .

mocha.opts

--ui bdd
--recursive

Wenn es gibt gemeinsame Module , dass Sie über alle Dateien aufnehmen möchten, können Sie dies mit dem Add - common.jsDatei. Dateien im Stammverzeichnis des testVerzeichnisses werden vor Dateien in verschachtelten Verzeichnissen ausgeführt.

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
quelle
3
Würde es jemandem etwas ausmachen, Code für Dateien in den Controllern und Modellverzeichnissen hinzuzufügen? Es wäre toll, ein vollständiges Beispiel zu haben.
Gavin
@ Gavin - dies werden nur Testanzüge sein, damit sie enthaltendescribe('mytest', function() { /* ..... etc */ });
Ian Jamieson
7

Ich weiß, dass dies ein alter Beitrag ist, aber ich wollte mich auf eine für mich gute Lösung einlassen, die der von OP vorgeschlagenen Methode sehr ähnlich ist.

Das Projekt, an dem ich arbeite, ist gut getestet und die Tests wachsen weiter. Ich habe es letztendlich verwendet, requireweil es synchron ist und es daher ein bisschen einfacher macht, Ihre Tests ohne zu große Änderungen in der Architektur zu erstellen:

// 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
quelle
2

Ich hatte ein ähnliches Problem, bei dem ich eine Reihe von Tests für Klassen in derselben Kategorie hatte, und ich wollte sie zusammenfassen, um das Anzeigen in einer IDE zu vereinfachen. Alle meine Tests und mein Code verwendeten bereits ES6-Module - ich wollte nicht alle neu schreiben, um sie requirewie in anderen Beispielen zu verwenden.

Ich habe es gelöst, indem ich meine "Gruppierung" describeexportiert und dann in meine Testdateien importiert und programmgesteuert zu den importierten hinzugefügt habe describe. Am Ende habe ich eine Hilfsmethode entwickelt, um alle Klempnerarbeiten zu abstrahieren.

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

In Einzeltests:

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

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

    it('a test', () => {
        ...
    });
})
Jon Senchyna
quelle
-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();
    }
} );
Mike
quelle
3
Fügen Sie am besten eine Beschreibung zusammen mit dem Code hinzu, damit andere feststellen können, ob dies eine akzeptable Antwort ist.
Suever
2
Warum die Schleife? Was ist drin ./Test.js? Wer weiß? Für die Aufzeichnung bin ich derzeit der beste Antwortende im Mokka- Tag. Ich kenne Mokka in- und auswendig, aber ich kann diese Antwort nicht verstehen.
Louis
@ Louis scheint die Tests n- mal mit der Schleife ausführen zu wollen .
Akash Agarwal