juntando testes de vários arquivos com mocha.js

87

Estou tentando juntar todos os testes de vários arquivos em um arquivo, algo assim:

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

Tenho quase certeza de que essa não é a melhor maneira de participar de testes, estou tendo dificuldade em encontrar exemplos de como fazer isso: s

coiso
fonte
1
Curioso, por que os testes precisam ser agrupados em um arquivo?
thgaskell
2
Para compartilhar variáveis ​​locais e organização
coiso
Pode fazer mais sentido se você incluir os testes na pergunta. Parece que você está inclinado para os testes de integração (em oposição aos testes de unidade). Geralmente, você não precisa compartilhar variáveis ​​entre os testes.
thgaskell
2
E o grande problema é que eu preferiria ter 20 arquivos do que 1 arquivo
enorme
2
Além disso, se você olhar como o Mocha lida com suítes com o conceito de .only()que pode ser útil ser capaz describe.only()de ainda executar um diretório inteiro de testes. Foi isso que me trouxe aqui.
Chris

Respostas:

113

Se você deseja incluir vários módulos em sua describehierarquia como está fazendo em sua pergunta, o que você está fazendo é basicamente isso , a menos que queira escrever um carregador de teste personalizado para o Mocha. Escrever o carregador personalizado não seria mais fácil ou tornaria seu código mais claro do que o que você já possui.

Aqui está um exemplo de como eu mudaria algumas coisas. O testsubdiretório neste exemplo é 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");
    });
});

A importTestfunção é apenas mostrar como seria possível lidar com a repetição de importação de vários módulos sem ter que redigitar describe(... require...tudo a cada vez. O commonmódulo destina-se a conter o que você precisa usar em vários módulos do conjunto de testes. Na verdade, não estou usando, topmas pode ser usado lá, se necessário.

Notarei aqui que o beforeEachexecutará seu código antes de cada teste registrado com itse eles aparecem dentro do describeem topou em algum dos módulos importados . Com --recursive, o beforeEachcódigo teria que ser copiado em cada módulo ou talvez você tivesse um beforeEachgancho em cada módulo que chama uma função importada de um módulo comum.

Além disso, o aftergancho será executado após todos os testes do conjunto. Isso não pode ser replicado com --recursive. Se você usar --recursivee adicionar o código de aftera cada módulo, ele será executado uma vez por módulo em vez de apenas uma vez para todo o teste.

Fazer com que todos os testes apareçam sob um único toptítulo não pode ser replicado usando --recursive. Com --recursivecada arquivo poderia ter, describe("top"mas isso criaria um novo toptítulo para cada arquivo.

common.js:

var chai = require("chai");

var options = {
    foo: "foo"
};

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

Usar um módulo denominado commoncomo este é algo que fiz em alguns dos meus conjuntos de teste para evitar ter de requireum monte de coisas repetidamente e manter variáveis globais somente leitura ou funções que não mantêm o estado. Eu prefiro não poluir o globalobjeto como na resposta de thgaskell porque este objeto é realmente global e acessível mesmo em bibliotecas de terceiros que seu código pode estar carregando. Isso não é algo que considero aceitável em meu 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 () {});
Louis
fonte
3
Embora eu concorde que você não deve poluir o globalescopo, eu uso isso para as bibliotecas de asserção para manter os arquivos de teste mais limpos. Não é como se você estivesse substituindo global.process. As variáveis ​​locais serão substituídas, a globalmenos que outras bibliotecas estejam chamando explicitamente, o global.XYZque é improvável. Ele dura apenas durante os testes. Não me machucou ainda, mas
avisarei
Qual é a diferença entre importTeste chamar, require('path')()por exemplo?
CherryNerd
@CreasolDev A importTestfunção é apenas uma função de conveniência. O importante que ele faz é agrupar a requirechamada em um describebloco. É importante que a requirechamada seja agrupada, describecaso contrário, os módulos não serão isolados em seu próprio bloco e qualquer gancho definido pelo arquivo importado será definido no bloco errado. Se importTestfosse substituído por uma chamada direta para requiresem empacotamento describe, os módulos a/ae b/bcompartilhariam os ganchos. Por exemplo, um beforeEachgancho colocado em b/btambém seria executado antes de cada teste em a/a.
Louis
1
Eu NÃO executaria nenhuma lógica como beforeEach em sua descrição de nível superior. Deixe cada arquivo fazer suas próprias "coisas" antes de cada. Você estará acoplando seus testes uns aos outros e implementações não relacionadas se fizer isso.
PositiveGuy
1
Eu também faria o empacotamento de descreve em seus respectivos arquivos, não na função importTest. As descrições de nível superior em cada arquivo respectivo devem descrever a finalidade de seus conjuntos de testes de qualquer maneira
PositiveGuy
35

Embora isso possa não estar diretamente relacionado à pergunta, a resposta que eu estava procurando era:

$ mocha --recursive

Irá executar todos os testes nos subdiretórios da pasta "test". Arrumado. Evita ter que manter uma lista de testes que desejo carregar e, na verdade, apenas sempre executar tudo.

Ian Jamieson
fonte
3
Melhor resposta! Muito mais simples do que outras soluções propostas.
caiosm1005
12
@ caiosm1005 Essa resposta não está realmente resolvendo o problema apresentado pelo OP . Claro, se você não precisa fazer o que o OP deseja , você deve usar isso. No entanto, se você quiser agrupar cada arquivo de teste em vários describeblocos, describeblocos que abrangem arquivos --recursivenão farão isso. Visto que não resolve o problema do OP, não o chamaria de "melhor".
Louis
@louis - Eu acredito que você pode empacotar cada arquivo separado em describeblocos
Ian Jamieson
4
@IanJamieson O OP está tentando ter vários arquivos cobertos por um único describe bloco. Veja a pergunta. O describebloco "Controladores" deve abranger os testes de ./controllertests/messages.jse ./controllertests/users.js. Bater --recursiveem uma invocação Mocha não cria um describe("Controllers"bloqueio mágico .
Louis
3
@Louis Só estou tentando ajudar. Desculpe se te ofendi ao tentar criar describeblocos magicamente - o que eu realmente aprendi a fazer com o próprio Dumbledore.
Ian Jamieson
16

Não há nada que impeça você de executar vários arquivos de teste. Geralmente, cada teste não deve ser dependente dos resultados de outro teste, portanto, compartilhar variáveis ​​não é algo que você gostaria de fazer.

Aqui está um exemplo de como você pode organizar seus arquivos de teste.

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

Em seguida, dentro do seu mocha.optsarquivo, certifique-se de definir a --recursiveopção.

mocha.opts

--ui bdd
--recursive

Se houver são módulos comuns que você deseja incluir em todos os arquivos, você pode acrescentar que para o common.jsarquivo. Os arquivos na raiz do testdiretório serão executados antes dos arquivos em diretórios aninhados.

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
fonte
3
Alguém se importaria de adicionar código para arquivos nos diretórios de controladores e modelos? Seria ótimo ter um exemplo completo.
Gavin,
@Gavin - serão apenas trajes de teste para conterdescribe('mytest', function() { /* ..... etc */ });
Ian Jamieson
8

Sei que este é um post antigo, mas queria entrar na conversa com o que tem sido uma boa solução para mim, muito semelhante ao método proposto pelo OP.

O projeto no qual estou trabalhando está bem testado e os testes continuam crescendo. Acabei usando requireporque é síncrono e, portanto, torna um pouco mais fácil compor seus testes sem muitas mudanças na arquitetura:

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

Eu tive um problema semelhante onde eu tinha um monte de testes para classes na mesma categoria e queria agrupá-los para tornar mais fácil visualizá-los em um IDE. Todos os meus testes e código já estavam usando módulos ES6 - eu não queria reescrever todos eles para usarrequire como vi em outros exemplos.

Eu resolvi isso tendo meu "agrupamento" describeexportado e, em seguida, importando-o para meus arquivos de teste e adicionando-os programaticamente ao arquivo importadodescribe . Acabei criando um método auxiliar para abstrair todo o encanamento.

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

Em testes individuais:

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

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

    it('a test', () => {
        ...
    });
})
Jon Senchyna
fonte
-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
fonte
3
É melhor adicionar uma descrição junto com o código para que outras pessoas possam determinar se essa é uma resposta aceitável.
Suever 01 de
2
Por que o loop? O que está dentro ./Test.js? Quem sabe? Só para constar, sou atualmente o que mais responde na tag mocha . Eu conheço Mocha por dentro e por fora, mas não consigo entender essa resposta.
Louis
@Louis parece que queria executar os testes n vezes usando o loop.
Akash Agarwal