jonction de tests à partir de plusieurs fichiers avec mocha.js

87

J'essaie de joindre tous les tests de plusieurs fichiers dans un seul fichier, quelque chose comme ceci:

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

Je suis à peu près sûr que ce n'est pas la meilleure façon de participer à des tests, j'ai du mal à trouver des exemples de la façon de procéder: s

coiso
la source
1
Curieux, pourquoi les tests doivent-ils être réunis dans un seul fichier?
thgaskell
2
Pour partager les variables locales et l'organisation
coiso
Cela peut avoir plus de sens si vous incluez les tests dans la question. Il semble que vous vous penchez peut-être vers les tests d'intégration (par opposition aux tests unitaires). En général, vous ne devriez pas avoir besoin de partager des variables entre les tests.
thgaskell
2
Et le gros problème est que je préférerais avoir 20 fichiers plutôt qu'un fichier
huuuuge
2
De plus, si vous regardez comment Mocha gère les suites avec le concept de .only()celui-ci, il peut être utile de pouvoir describe.only()continuer à exécuter tout un répertoire de tests. C'est ce qui m'a amené ici.
Chris

Réponses:

113

Si vous souhaitez inclure plusieurs modules dans votre describehiérarchie comme vous le faites dans votre question, ce que vous faites est à peu près tout , à moins que vous ne souhaitiez écrire un chargeur de test personnalisé pour Mocha. L'écriture du chargeur personnalisé ne serait pas plus facile ni ne rendrait votre code plus clair que ce que vous avez déjà.

Voici un exemple de la façon dont je changerais certaines choses. letest sous-répertoire de cet exemple est organisé comme suit:

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

La importTestfonction est juste de montrer comment il serait possible de gérer la répétition de l'importation de plusieurs modules sans avoir à retaper l'ensembledescribe(... require... à chaque fois. Le commonmodule est destiné à contenir ce que vous devez utiliser dans plusieurs modules de la suite de tests. Je ne l'utilise pas réellement topmais il pourrait être utilisé là-bas, si nécessaire.

Je noterai ici que le beforeEachexécutera son code avant chaque test enregistré avec its'ils apparaissent à l'intérieur du describein topou dans l' un des modules importés . Avec--recursive , le beforeEachcode devrait être copié dans chaque module ou peut-être auriez-vous un beforeEachhook dans chaque module qui appelle une fonction importée d'un module commun.

De plus, le afterhook s'exécutera après tous les tests de la suite. Cela ne peut pas être répliqué avec --recursive. Si vous utilisez --recursiveet ajoutez le code deafter à chaque module, il sera exécuté une fois par module plutôt qu'une seule fois pour tout le test.

L'affichage de tous les tests sous un seul en- toptête ne peut pas être répliqué à l'aide de --recursive. Avec --recursivechaque fichier pourrait avoirdescribe("top" mais cela créerait un nouveau toptitre pour chaque fichier.

common.js:

var chai = require("chai");

var options = {
    foo: "foo"
};

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

L'utilisation d'un module nommé commoncomme celui-ci est quelque chose que j'ai fait dans certaines de mes suites de tests pour éviter d'avoir à faire requireun tas de choses encore et encore et pour contenir des variables globales en lecture seule ou des fonctions qui ne conservent pas l'état. Je préfère ne pas polluer leglobal objet comme dans la réponse de thgaskell car cet objet est vraiment global et accessible même dans les bibliothèques tierces que votre code peut charger. Ce n'est pas quelque chose que je trouve acceptable dans mon code.

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
la source
3
Bien que je convienne que vous ne devriez pas polluer la globalportée, je l'utilise pour les bibliothèques d'assertions afin de garder les fichiers de test plus propres. Ce n'est pas comme si vous écrasiez global.process. Les variables locales remplaceront à globalmoins que d'autres bibliothèques n'appellent explicitement global.XYZce qui est peu probable. Cela ne dure que pendant la durée des tests. Cela ne m'a pas encore fait mal, mais je vous ferai savoir le moment où ça me mord dans le cul :)
thgaskell
Quelle est la différence entre importTestet appeler require('path')()par exemple?
CherryNerd
@CreasolDev La importTestfonction n'est qu'une fonction pratique. L'important, c'est d'envelopper l' requireappel dans un describebloc. Il est important que l' requireappel soit encapsulé, describesinon les modules ne seront pas isolés dans leur propre bloc et tout hook défini par le fichier importé sera défini sur le mauvais bloc. Si a importTestété remplacé par un appel direct à requiresans emballage describe, les modules a/aet b/bpartageraient des crochets. Par exemple, un beforeEachhook défini dans b/bserait également exécuté avant chaque test dans a/a.
Louis
1
Je ne lancerais aucune logique telle que beforeEach dans votre description de niveau supérieur. Laissez chaque fichier faire son propre avant chaque "truc". Vous couplerez vos tests les uns aux autres et à une implémentation indépendante si vous faites cela.
PositiveGuy
1
Je ferais également l'emballage des descriptions dans leurs fichiers respectifs, pas dans la fonction importTest. Le niveau supérieur décrit dans chaque fichier respectif devrait de toute façon décrire le but de leur suite de tests
PositiveGuy
35

Bien que cela ne soit pas directement lié à la question, la réponse que je recherchais était:

$ mocha --recursive

Exécutera tous les tests dans les sous-répertoires du dossier "test". Soigné. Évite d'avoir à maintenir une liste de tests que je veux charger et de toujours tout exécuter.

Ian Jamieson
la source
3
Meilleure réponse! Beaucoup plus simple que les autres solutions proposées.
caiosm1005
12
@ caiosm1005 Cette réponse ne résout pas réellement le problème présenté par l'OP . Bien sûr, si vous n'avez pas besoin de faire ce que l'OP veut faire , vous devriez l'utiliser. Cependant, si vous souhaitez envelopper chaque fichier de test dans plusieurs describeblocs, les describeblocs qui couvrent les fichiers --recursivene le feront pas. Vu que cela ne résout pas le problème de l'OP, je ne l'appellerais pas «meilleur».
Louis
@louis - Je crois que vous pouvez envelopper chaque fichier séparé en describeblocs
Ian Jamieson
4
@IanJamieson L'OP essaie d'avoir plusieurs fichiers couverts par un seul describe bloc. Regardez la question. Le describebloc "Contrôleurs" doit englober les tests de ./controllertests/messages.jset ./controllertests/users.js. Gifler --recursivesur une invocation de Mocha ne crée pas comme par magie un describe("Controllers"bloc.
Louis
3
@Louis J'essaye juste d'aider. Désolé si je vous ai offensé en essayant de créer des describeblocs par magie - ce que j'ai appris à faire de Dumbledore lui-même.
Ian Jamieson
16

Rien ne vous empêche d'exécuter plusieurs fichiers de test. En règle générale, chaque test ne doit pas dépendre des résultats d'un autre test, donc partager des variables n'est pas quelque chose que vous voudriez faire.

Voici un exemple de la manière dont vous pourriez organiser vos fichiers de test.

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

Ensuite, à l'intérieur de votre mocha.optsfichier, assurez-vous de définir l' --recursiveoption.

moka.opts

--ui bdd
--recursive

S'il y a des modules communs que vous souhaitez inclure dans tous les fichiers, vous pouvez l' ajouter au common.jsfichier. Les fichiers à la racine du testrépertoire seront exécutés avant les fichiers dans les répertoires imbriqués.

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
la source
3
Quelqu'un voudrait-il ajouter du code pour les fichiers dans les répertoires des contrôleurs et des modèles? Ce serait formidable d'avoir un exemple complet.
Gavin
@Gavin - ce ne seront que des combinaisons de test, donc elles contiendraientdescribe('mytest', function() { /* ..... etc */ });
Ian Jamieson
8

Je sais que c'est un ancien post mais je voulais faire écho à ce qui a été une bonne solution pour moi, très similaire à la méthode proposée par OP.

Le projet sur lequel je travaille est bien testé et les tests ne cessent de croître. J'ai fini par utiliser requirecar il est synchrone et donc facilite un peu la composition de vos tests sans trop de changement d'architecture:

// 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
la source
2

J'ai eu un problème similaire où j'avais un tas de tests pour des classes de la même catégorie et je voulais les regrouper pour faciliter leur visualisation dans un IDE. Tous mes tests et mon code utilisaient déjà des modules ES6 - je ne voulais pas tous les réécrire pour les utiliser requirecomme je l'ai vu dans d'autres exemples.

Je l'ai résolu en describeexportant mon «regroupement» , puis en l'important dans mes fichiers de test et en les ajoutant par programme à l'importation describe. J'ai fini par créer une méthode d'aide pour faire abstraction de toute la plomberie.

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

Dans les tests individuels:

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

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

    it('a test', () => {
        ...
    });
})
Jon Senchyna
la source
-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
la source
3
Il est préférable d'ajouter une description avec le code afin que les autres puissent déterminer s'il s'agit d'une réponse acceptable.
Suever
2
Pourquoi la boucle? Qu'est-ce que c'est ./Test.js? Qui sait? Pour mémoire, je suis actuellement le premier répondant dans la balise moka . Je connais Mocha de fond en comble, mais je ne peux pas comprendre cette réponse.
Louis
@Louis semble vouloir exécuter les tests n fois en utilisant la boucle.
Akash Agarwal