uniendo pruebas de varios archivos con mocha.js

87

Estoy tratando de unir todas las pruebas de varios archivos en un solo archivo, algo como esto:

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

Estoy bastante seguro de que esta no es la mejor manera de unirme a pruebas, tengo algunas dificultades para encontrar ejemplos de cómo hacer esto: s

coiso
fuente
1
Curioso, ¿por qué es necesario unir las pruebas en un solo archivo?
thgaskell
2
Para compartir las variables locales y la organización
coiso
Podría tener más sentido si incluye las pruebas en la pregunta. Parece que se está inclinando hacia las pruebas de integración (a diferencia de las pruebas unitarias). Por lo general, no debería ser necesario compartir variables entre pruebas.
thgaskell
2
Y el gran problema es que preferiría tener como 20 archivos que 1 archivo
huuuuge
2
Además, si observa cómo Mocha maneja las suites con el concepto de .only(), podría ser útil poder poner describe.only()en ejecución un directorio completo de pruebas. Eso es lo que me trajo aquí.
Chris

Respuestas:

113

Si desea incluir varios módulos en su describejerarquía como lo está haciendo en su pregunta, lo que está haciendo es prácticamente eso , a menos que desee escribir un cargador de prueba personalizado para Mocha. Escribir el cargador personalizado no sería más fácil ni haría su código más claro de lo que ya tiene.

Aquí hay un ejemplo de cómo cambiaría algunas cosas. El testsubdirectorio en este ejemplo está organizado como:

.
└── 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 importTestfunción es solo para mostrar cómo sería posible manejar la repetición de la importación de varios módulos sin tener que volver a escribir todo describe(... require...cada vez. El commonmódulo está destinado a contener lo que necesita usar en varios módulos del conjunto de pruebas. En realidad, no lo estoy usando, toppero podría usarse allí, si es necesario.

Voy a señalar aquí que la beforeEachva a ejecutar su código antes de cada prueba única registrada con itsi aparecen dentro de la describeen top, o que aparecen en cualquiera de los módulos importados . Con --recursive, el beforeEachcódigo tendría que copiarse en cada módulo o tal vez tendría un beforeEachgancho en cada módulo que llama a una función importada de un módulo común.

Además, el aftergancho se ejecutará después de todas las pruebas en la suite. Esto no se puede replicar con --recursive. Si usa --recursivey agrega el código de aftera cada módulo, se ejecutará una vez por módulo en lugar de solo una vez para toda la prueba.

El hecho de que todas las pruebas aparezcan bajo un solo topencabezado no se puede replicar con --recursive. Con --recursivecada archivo podría haberlo hecho, describe("top"pero esto crearía un nuevo topencabezado para cada archivo.

common.js:

var chai = require("chai");

var options = {
    foo: "foo"
};

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

Usar un módulo llamado commonasí es algo que he hecho en algunas de mis suites de prueba para evitar tener que hacer requireun montón de cosas una y otra vez y mantener variables o funciones globales de solo lectura que no mantienen el estado. Prefiero no contaminar el globalobjeto como en la respuesta de thgaskell porque este objeto es verdaderamente global y accesible incluso en bibliotecas de terceros, su código puede estar cargando. Esto no es algo que encuentre aceptable en mi código.

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 () {});
Luis
fuente
3
Si bien estoy de acuerdo en que no debe contaminar el globalalcance, lo uso para las bibliotecas de aserción para mantener más limpios los archivos de prueba. No es como si estuvieras sobrescribiendo global.process. Las variables locales se anularán a globalmenos que otras bibliotecas llamen explícitamente, lo global.XYZcual es poco probable. Solo dura la duración de las pruebas. No me ha hecho daño todavía, pero te lo haré saber en el momento en que me muerda el culo :)
thgaskell
¿Cuál es la diferencia entre importTesty llamar, require('path')()por ejemplo?
CherryNerd
@CreasolDev La importTestfunción es solo una función de conveniencia. Lo importante que hace es envolver la requirellamada en un describebloque. Es importante que la requirellamada se envuelva, de lo describecontrario, los módulos no se aislarán en su propio bloque y cualquier enlace establecido por el archivo importado se establecerá en el bloque incorrecto. Si importTestse reemplazó con una llamada directa a requiresin un ajuste describe, entonces los módulos a/ay b/bcompartirían ganchos. Por ejemplo, un beforeEachgancho establecido b/btambién se ejecutaría antes de cada prueba en a/a.
Louis
1
NO ejecutaría ninguna lógica como antes. Cada uno en su nivel superior describe. Deje que cada archivo haga lo suyo antes de cada "cosas". Si hace esto, acoplará sus pruebas entre sí y la implementación no relacionada.
PositiveGuy
1
También haría el ajuste de las descripciones en sus respectivos archivos, no en la función importTest. Las descripciones de nivel superior en cada archivo respectivo deben describir el propósito de sus conjuntos de pruebas de todos modos
PositiveGuy
35

Aunque esto puede no estar directamente relacionado con la pregunta, la respuesta que estaba buscando era:

$ mocha --recursive

Ejecutará todas las pruebas en subdirectorios de la carpeta "prueba". Ordenado. Me ahorra tener que mantener una lista de pruebas que quiero cargar y, en realidad, siempre ejecutarlo todo.

Ian Jamieson
fuente
3
¡La mejor respuesta! Mucho más simple que otras soluciones propuestas.
caiosm1005
12
@ caiosm1005 Esta respuesta en realidad no resuelve el problema presentado por el OP . Claro, si no necesita hacer lo que el OP quiere hacer , entonces debería usar esto. Sin embargo, si desea envolver cada archivo de prueba en varios describebloques, los describebloques que abarcan archivos --recursiveno lo harán. Dado que no resuelve el problema del OP, no lo llamaría "mejor".
Louis
@louis - Creo que puedes envolver cada archivo por separado en describebloques
Ian Jamieson
4
@IanJamieson El OP está tratando de tener varios archivos cubiertos por un solo describe bloque. Mira la pregunta. El describebloque "Controladores" debe abarcar las pruebas de ./controllertests/messages.jsy ./controllertests/users.js. Dar --recursiveuna palmada en una invocación de Mocha no crea mágicamente un describe("Controllers"bloqueo.
Louis
3
@Louis Solo intento ayudar. Perdón si te ofendí al intentar crear describebloques mágicamente , lo cual aprendí a hacer del mismo Dumbledore.
Ian Jamieson
16

No hay nada que le impida ejecutar varios archivos de prueba. Generalmente, cada prueba no debe depender de los resultados de otra prueba, por lo que compartir variables no es algo que le gustaría hacer.

Este es un ejemplo de cómo podría organizar sus archivos de prueba.

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

Entonces dentro de tu mocha.opts archivo, asegúrese de configurar la --recursiveopción.

mocha.opts

--ui bdd
--recursive

Si no están módulos comunes que desea incluir en todos los archivos, se puede añadir que el common.jsarchivo. Los archivos en la raíz del testdirectorio se ejecutarán antes que los archivos en directorios anidados.

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
fuente
3
¿A alguien le importaría agregar código para archivos en los directorios de controladores y modelos? Sería genial tener un ejemplo completo.
Gavin
@Gavin - estos serán solo trajes de prueba para que contengandescribe('mytest', function() { /* ..... etc */ });
Ian Jamieson
8

Sé que esta es una publicación antigua, pero quería intervenir con lo que ha sido una buena solución para mí, muy similar al método propuesto por OP.

El proyecto en el que estoy trabajando está bien probado y las pruebas siguen creciendo. Terminé usando requireporque es sincrónico y, por lo tanto, hace que sea un poco más fácil componer sus pruebas sin demasiados cambios en la arquitectura:

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

Tuve un problema similar en el que tenía un montón de pruebas para clases en la misma categoría y quería agruparlas para facilitar su visualización en un IDE. Todas mis pruebas y código ya usaban módulos ES6; no quería reescribirlos todos para usarlos requirecomo vi en otros ejemplos.

Lo resolví describeexportando mi "agrupación" y luego importándolo a mis archivos de prueba y agregándolos programáticamente al archivo importado describe. Terminé creando un método auxiliar para abstraer todas las tuberías.

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

En pruebas individuales:

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

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

    it('a test', () => {
        ...
    });
})
Jon Senchyna
fuente
-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();
    }
} );
Miguel
fuente
3
Es mejor agregar una descripción junto con el código para que otros puedan determinar si esta es una respuesta aceptable.
Suever
2
¿Por qué el bucle? ¿Qué hay dentro ./Test.js? ¿Quién sabe? Para que conste, actualmente soy el principal respondedor en la etiqueta mocha . Conozco a Mocha por dentro y por fuera, pero no puedo entender esta respuesta.
Louis
@Louis parece que quería ejecutar las pruebas n veces usando el ciclo.
Akash Agarwal