Mocha: opt-out de globais

Criado em 20 ago. 2013  ·  43Comentários  ·  Fonte: mochajs/mocha

@isaacs está reclamando que os testes Mocha não são node -able. Acho que é uma reclamação boba, mas acho que ele representa uma minoria vocal, então calá-los seria possivelmente valioso.

Aqui está o que eu imagino: em vez de ser capaz de usar, por exemplo, describe , it , etc. por padrão, você faria

var mochaBDD = require("mocha/bdd");
var describe = mochaBDD.describe;
var it = mochaBDD.it;

// ok, stupid boilerplate is over but at least I don't have to use a test runner, woohoo!

Alternativamente, pode ser apenas

require("mocha/bdd");

e isso configuraria alguns globais para você.

O que você acha?

feature help wanted

Comentários muito úteis

+1 para:
import { describe, before, it } from 'mocha';

Todos 43 comentários

Mesmo se ele apenas despejasse sua porcaria em todo o espaço global quando você require('mocha') , seria melhor do que o estado atual das coisas.

Não acho que seja bobo, mas há compensações em tudo. Eu ficaria feliz com algo assim:

var Mocha = require('mocha');
var mocha = new Mocha(options);
mocha.describe('blah blah', function....

ninguém o usaria, mas pelo menos seria uma maneira mais limpa de implementar o que temos atualmente. Haveria um _ton_ de clichê que todos teriam que configurar a cada vez, mas se pudéssemos restringi-los a APIs CLI-ish, estaria tudo bem. Mesmo se houvesse lib / cli.js que acabou de ser aprovado no ARGV, mas ainda duvido que alguém o usaria, você pode usá-lo sem a CLI razoavelmente fácil, mas isso ilustra que ninguém realmente quer além de alguns casos extremos.

@visionmedia isso parece muito bom. O motivo pelo qual sugeri require("mocha/bdd") ou similar é que seria muito fácil de implementar em termos de Mocha existente, mas sim, o seu provavelmente é melhor. (Você poderia imaginar usá-lo para, por exemplo, executar vários conjuntos de testes de uma vez ou algo assim. Bem, isso provavelmente seria interrompido por causa do uso de process.on ('uncaughtException'), mas você entende o que quero dizer.)

Posso tentar um pull request um dia.

Este é um ótimo exemplo em que um pouco de JavaScript futuro por meio de atribuição de desestruturação pode ajudar muito.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.7#Destructuring_assignment_% 28Merge_into_own_page.2Fsection% 29

var [describe,it,beforeEach,afterEach] = new require("mocha")(options);

Só queria que houvesse suporte de harmonia de nó para isso.

É a ideia disso poder fazer

node test/a-mocha-test.js

E fez esse teste?

@glenjamin yup

Tendo tentado fazer isso sozinho no nodespec, o problema não era realmente disponibilizar as várias funções, o problema era descobrir como / quando começar a executar - ainda precisaríamos de uma abordagem definir / executar em duas passagens.

As opções que descobri foram:
1) fazer com que cada usuário adicione algo como mocha.exec () na parte inferior de cada arquivo que eles podem querer executar isoladamente
2) Aguarde até que o núcleo adicione algo como process.on ('sair'), mas quando o loop de evento ainda estiver disponível
3) assumir que cada arquivo tem apenas um bloco de descrição de nível superior e começar a execução quando terminar

(1) é provavelmente o mais bonito, que poderia ser assim:

var run = require('mocha/required');

describe('blah blah' , function() {
// ..
});

run();

Isso não parece adicionar muito a fazer node ./node_modules/.bin/_mocha test/a-mocha-test.js quando você realmente precisa executar sem o invólucro mocha .

Acho que isso não se encaixa mais com a forma como o mocha é usado atualmente. Como este tópico está inativo há mais de um ano, vou encerrá-lo por enquanto.

Se alguém ainda estiver interessado nisso, sinta-se à vontade para comentar ou propor uma solicitação pull e consideraremos :)

Isso poderia ser reaberto? Ainda estou muito interessado nisso. Não poder fazer apenas node test.js testes de mocha foi uma das principais razões pelas quais eu e meus colegas da Mapbox temos mudado do mocha para equipamentos como fita e torneira. Pessoalmente, prefiro ficar com o mocha, mas acho esse argumento da "capacidade do nó" um tanto convincente.

Editar: o argumento "capacidade do nó" foi elaborado por @tmcw aqui .

Para maior clareza, a falta de uma interface require capaz não é o principal bloqueador aqui, da perspectiva do usuário. O seguinte já está documentado para funcionar:

var describe = require('mocha').describe;
var before = require('mocha').before;
var it = require('mocha').it;

E fica melhor com ES6:

import { describe, before, it } from 'mocha';

O problema é o fato de que isso só funciona quando executado por meio do binário mocha .

Oh, obrigado pelo esclarecimento. :) Se já oferecemos essa capacidade quando executado usando o binário mocha, concordo que seria bom fazer o mesmo com o node. Vou dar uma olhada nisso. Obrigado!

Agora que o nó tem um gancho beforeExit, isso parece bastante factível.

Precisaria ser algo como

var { describe, it } = require('mocha/auto')

Sim. Como TJ disse, o problema é que mesmo se tivéssemos que tornar tudo "capaz de nodos", pode ser necessário muito clichê para ser útil.

Contudo...

@glenjamin, obrigado pelo heads-up sobre beforeExit . Acho que podemos aproveitar isso. As pessoas provavelmente ainda reclamariam que é muita "mágica" ...

Eu adicionei uma prova de conceito a 3cdd4a04c48193cceac6af7db72af06d380014a9

de qualquer forma, precisaria de um pouco de trabalho, mas acho que podemos generalizar facilmente para todas as interfaces. não há suporte aqui para Growl, mocha.opts ou várias outras coisas relacionadas a process , como códigos de saída. Devemos extrair algum código de bin/_mocha e reutilizá-lo aqui. Então, poderíamos ter suporte de argumento também. Por exemplo:

$ node test/my-test.js --reporter=dot

Se alguém tiver alguma sugestão, deixe-me saber

Acho que extrair algumas das coisas em bin/_mocha em algum tipo de CLI seria bastante sensato de qualquer maneira.

Ainda não tenho certeza se isso é realmente uma adição útil?

Existe muita diferença prática entre qualquer um dos seguintes?

node test/test.js
./node_modules/.bin/mocha test/test.js
PATH=./node_modules/.bin:$PATH mocha test/test.js
npm test -- test/test.js

Tenho a impressão de que as pessoas que não estão usando o mocha porque não podem executar arquivos de teste via node são simplesmente pessoas que não gostam do mocha e estão procurando uma desculpa! Mocha não tem que ser para todos: sorria:

O mesmo aqui. Estou usando jspm e SystemJS e não consigo import Mocha em meus testes que estão sendo executados no navegador.

import { describe, before, it } from 'mocha';

Não pode ser usado quando usado em navegadores.

@glenjamin isso só precisa acontecer. O Mocha deve ter uma API programática central. Um bom efeito colateral disso são os testes com capacidade de nó. O ambiente no qual é executado consumirá a API programática, seja um navegador, CLI ou algo totalmente diferente. Mocha.app, alguém? :)

Interessado neste recurso!

+1 para:
import { describe, before, it } from 'mocha';

Atualmente temos const {describe, it} = require('mocha') , mas não é necessário porque os globais já existem.

Se estivermos preocupados com o suporte do navegador, podemos sempre fazer const {describe, it} = window.mocha . Já temos que fazer isso para o chai .

bem, const {describe, it} = window também, suponho.

isso é tão fundamental para a arquitetura do mocha que exporta o objeto global quando empacotado.

Não acho que o mocha deva definir qualquer outro global além de um único namespace, e talvez apenas para navegadores.

o mocha executável continuará a usar globais; isso provavelmente nunca mudará.

o que podemos (e devemos) almejar é a capacidade de facilmente executar testes mocha por meio de node sem poluir o namespace global. é disso que trata este problema.

infelizmente, é uma bola de barbante do tamanho do Texas que ninguém teve tempo ou energia para desfazer, o que deve ser aparente com a idade desta edição ...

o mocha executável continuará a usar globais; isso provavelmente nunca mudará.

Embora eu não saiba nada sobre como o mocha é estruturado, posso afirmar que essa única escolha de design é a origem do problema.

provavelmente sim!

Na verdade, dei uma segunda olhada no código, descobri (mais ou menos) o que tinha feito de errado da última vez que tentei hackear globals, pensei sobre o que seria necessário para que os arquivos de teste executassem com node test/someFile.js , e acredito que sei como fazer os dois - com a ressalva de que fazer isso sem triplicar o número de casos extremos provavelmente exigirá a quebra de alguns casos extremos existentes. Em mais detalhes, meus pensamentos sobre o design são:

  • evitando globais

    • Mesmo em um semver major, não podemos quebrar a esmagadora maioria do uso do Mocha que usa os globais. A equipe poderia deixar seus empregos diurnos, nunca mais dormir, passar o resto de suas carreiras nisso e ainda literalmente não ter horas de trabalho suficientes para lidar com a inundação inimaginável de pedidos de suporte que isso acarretaria. Portanto: isso ficará atrás de uma bandeira, para sempre .

    • Atualmente, as interfaces BDD e TDD são especiais: ambas serão exportadas (e nenhuma outra interface) como require("mocha").it et al. (Observação: o motivo pelo qual isso não falha quando outras interfaces são selecionadas pode ser que o Mocha tem um bug em que, na maioria das circunstâncias, ele configurará a interface do BDD mesmo se outra for selecionada. # 2207) Eu quero me afastar disso.

    • Interfaces personalizadas (de terceiros) devem ser capazes de trabalhar com o esquema de exportação (contanto que usem o objeto de contexto emitido em vez de codificar global ).

    • Não podemos ter todas as interfaces possíveis adicionando suas funções a Mocha (o que require("mocha") exporta).

    • Se alguma vez consertarmos o bug em que a interface BDD está sempre configurada, será complicado também continuar exportando as interfaces BDD e TDD em Mocha outra interface é escolhida.

    • Você não deve configurar a IU do Mocha para TDD e usar a interface BDD ou vice-versa de qualquer maneira, a menos que talvez você faça require("mocha/tddInterface").test ou require("mocha/interface/bdd").it (não deve ser confundido com "mocha/lib/interfaces/<interface name>" onde as implementações ao vivo).

    • Poderíamos talvez manter o comportamento export-BDD-and-TDD-on-Mocha-se-possível para compatibilidade com versões anteriores até um semver-major. Não espero que muitas pessoas reclamarão quando o abandonarmos, visto que as pessoas que solicitaram e estão usando sabem mais sobre o que estão fazendo do que a maioria e não estão totalmente satisfeitas com isso de qualquer maneira (já que globais ainda estão lá).

  • capacidade de nó, ou seja, ser capaz de executar node test/someFile.js vez de mocha test/someFile.js

    • Embora seja menos onipresente do que os globais, não acho que podemos quebrar pessoas que estão usando var Mocha = require("mocha"); var mocha = new Mocha() etc. (ou seja, a "interface programática"). Teríamos que detectar isso e evitar a execução do Mocha "da maneira que permite o nó" ou então (mais fácil para nós, mas requer aceitação dos usuários) exigir que a capacidade do nó seja acessada por meio de uma importação diferente, por exemplo, require("mocha/nodeable").it ( .describe etc.). Uma importação especial em vez de pegar carona em require("mocha") seria mais fácil de implementar com segurança e se encaixaria em meu desejo (descrito e justificado na seção de evitar globais) de deixar de exportar interfaces no objeto Mocha exportado por require("mocha") .

    • Para ser capaz de node test/someFile.js , não é suficiente que someFile.js seja capaz de importar a interface do Mocha: o Mocha deve ser instanciado e, após todos os testes serem configurados, o Mocha deve ser executado. Em outras palavras, require("mocha/nodeable") deve instanciar o Mocha, bem como exportar a interface, e então ... após a execução do arquivo de teste, ele deve executar o Mocha.

    • Uma maneira de fazer isso seria executar o Mocha em process.on("beforeExit" , desde que o arquivo de teste não inicie nenhuma ação assíncrona.

    • Outra maneira de fazer isso seria executar o Mocha em process.nextTick ou setImmediate ou setTimeout(..., 0 /* or some small number */) .

    • Testes que configuram suas coisas com --delay e rodam de forma assíncrona com run() seriam um problema. Não para saber quando correr - é mais fácil, há run() - e nem mesmo para saber se --delay (veja abaixo ou forneça require("mocha/nodeable/delayed") ), mas sim porque run() só deve ser chamado em um arquivo de teste conforme projetado atualmente. Indiscutivelmente, esse design já é difícil de trabalhar e precisaríamos corrigi-lo para que run() deva ser chamado em todos os arquivos de teste de qualquer maneira, caso em que usá-lo com testes "capazes de nós" seria fácil depois consertando-o. Caso contrário ... não tenho certeza do que fazer.

    • Se quisermos permitir a chamada desses arquivos de teste por meio de mocha vez de node (ou seja, usar isso não força o uso de node ), então o que quer que seja require d tem que detectar quando a CLI está em uso e não instanciar o Mocha então. Isso seria mais fácil do que detectar quando a API programática está em uso - a CLI pode definir algum estado global que esses módulos verificariam, ou esses módulos podem verificar se a CLI é o arquivo que o Node executou.

    • Precisaríamos tornar as peças CLI mais modulares se quiséssemos suportar a passagem de sinalizadores do Mocha ao executar através do Node, ou usando mocha.opts , ou qualquer coisa que permitiria que os arquivos de teste com capacidade de nó usassem um comportamento diferente do padrão do Mocha comportamento.

  • Combinando essas duas restrições de ideias, proponho (e acredito que posso implementar) :

    • evitando globais

    • Em vez de passar às interfaces o objeto global para colocar suas funções, ele vai passar um "objeto de interface selecionado". (EDITADO PARA ADICIONAR: isso deve ser feito em lib/mocha.js e browser-entry.js . Pelo que eu sei, todas as partes globais da proposta são viáveis ​​no Node e no navegador. )

    • Posteriormente, ele copiará todo o conteúdo do objeto de interface selecionado em global .

    • Uma opção localInterface ( --local-interface na CLI) impedirá o Mocha de copiar do objeto de interface selecionado para global .

    • DEBATABLE: Inicialmente, para compatibilidade retroativa com require("mocha").it Mocha irá copiar funções TDD / BDD do objeto de interface selecionado para Mocha também, mas isto será descontinuado / removido posteriormente.



      • (NOTA: se impedirmos que o BDD seja configurado quando não estiver selecionado, talvez tenhamos que fazer ainda mais tratamento especial aqui para configurar o BDD em um objeto de interface selecionado fictício do qual eles podem ser copiados - um bom caso para descartar o comportamento como um todo, ele fica mais complexo quanto mais outras coisas consertamos / melhoramos, enquanto ainda tentamos oferecer suporte a isso.)



    • require("mocha/interface") exportará o objeto de interface selecionado.

    • require("mocha/interface/<specific interface>") chamará a interface especificada com um novo objeto de interface selecionado que será exportado posteriormente.

    • EDITADO PARA ADICIONAR: require("mocha/interface") também será uma propriedade no objeto mocha no navegador, mocha.interface , para suportar o uso do navegador sem um sistema de módulo.

    • capacidade de nó

    • require("mocha/nodeable/<specific interface>") irá instanciar o Mocha, configurar o Mocha para ser executado após o arquivo de teste (provavelmente usando setTimeout menos que delay seja usado, veja acima re. delay ) e exporte um objeto de interface selecionado para essa interface, de forma muito semelhante a require("mocha/interface/<specific interface>")

    • Se as opções CLI ou mocha.opts forem suportadas, require("mocha/nodeable") instanciaria o Mocha, configuraria o Mocha para ser executado após o arquivo de teste (veja acima) e executaria as mesmas etapas de configuração da interface do CLI, exportando o objeto de interface selecionado exatamente como require("mocha/interface") .

    • Se quisermos que os arquivos de teste com capacidade de nó também sejam mocha -able, então mocha/nodeable & mocha/nodeable/<specific interface> não é necessário, podemos adicionar a capacidade de nó em mocha/interface & mocha/interface/<specific interface> .

Pensamentos?

Na verdade, pensando melhor, proponho fechar este tíquete, com base na seguinte justificativa contra o uso de node <test file> no lugar de mocha <test file> :

  • Não estou ciente das vantagens que oferece.

    • Uma ideia possível é que ele executa apenas um único arquivo de teste, independentemente de quais globs estão em mocha.opts . Solução simples: coloque globs no script test em package.json : npm test executará todos eles e mocha ( node_modules/.bin/mocha , npx mocha ) executará apenas o (s) arquivo (s) de teste especificado (s). (Arquivos ou globs que devem ser executados para qualquer teste, independentemente de quantos arquivos de teste são executados, ainda devem ir em mocha.opts .)

    • Outra ideia possível é que permite contornar mocha.opts completamente. Isso deve ser possível agora usando mocha --opts /dev/null (ou no Windows mocha --opts nul ). Se por algum motivo não for, seria mais simples implementar um sinalizador CLI para pular o processamento de mocha.opts .

  • Pode ser implementado fora do Mocha; em vez de "mocha/nodeable" , apenas coloque o mesmo código usando a API programática do Mocha em um pacote hipotético "nodeable-mocha" .

    • Não estou dizendo que seria fácil de implementar em primeiro lugar, estou dizendo que não tenho conhecimento de nenhuma maneira que possa ser mais difícil de implementar por estar fora do Mocha.

Se descobrirmos qualquer vantagem significativa que seja mais fácil de obter no núcleo do que fora dele, podemos sempre reabrir.

No entanto, também proponho reabrir o tíquete de evitação de globais, porque:

  • Embora as vantagens não sejam relevantes (quantas outras bibliotecas estão usando globais chamadas describe , it etc.?), Elas são possíveis em princípio (a resposta a essa pergunta poderia ser maior de 0, porém poucos) - não despejar coisas no namespace global é, teoricamente, a coisa certa a se fazer.

    • Além disso, meu plano seria estender o mesmo comportamento a interfaces de terceiros que são escritas corretamente para usar o objeto de "contexto" emitido (para o qual o Mocha atualmente sempre usa o objeto global) em vez de codificar o objeto global; embora nossas próprias interfaces pareçam improváveis de interferir com outras bibliotecas, não podemos oferecer nenhuma garantia sobre outras interfaces, portanto, estaríamos tornando mais fácil para elas fazerem a coisa certa também.

  • Meu plano limparia muitos casos extremos relacionados no comportamento atual (por exemplo, estendendo-o para interfaces de terceiros, mas também consistência no Mocha em vez de invólucro especial nas interfaces BDD e TDD).
  • A única maneira fácil de implementá-lo fora do Mocha é usar a API programática do Mocha e fazer um monkey-patch nele , e isso deixaria algumas das inconsistências mencionadas e casos extremos também.

então você está sugerindo que o Mocha fornece uma maneira de executar testes com mocha mas atrás de uma bandeira que não poluirá global ?

parte do que torna o mocha mocha e parte de seu sucesso é que ele não exige clichês para importar coisas. apesar do dogma em torno da poluição do namespace global, há razões completamente válidas para fazer isso para a ergonomia do desenvolvedor.

então você está sugerindo que o Mocha fornece uma maneira de executar testes com o mocha, mas atrás de uma bandeira que não polui global?

Sim.

parte do que torna o mocha mocha e parte de seu sucesso é que ele não exige clichês para importar coisas. apesar do dogma em torno da poluição do namespace global, há razões completamente válidas para fazer isso para a ergonomia do desenvolvedor.

Não estou dizendo que discordo em geral, mas como um recurso opcional, posso ver onde alguns desenvolvedores podem querer importações em vez de globais (talvez eles queiram que seu módulo nomeie uma variável local context sem se referir acidentalmente para o global se eles esquecerem var , talvez eles gostariam de usar uma interface de terceiros onde esse tipo de colisão é ainda mais provável), e agora que descobri como fazer isso certo, é também é mais fácil de fazer no Mocha do que como um complemento de algum tipo (na verdade, se eu não for sugado por outra coisa, provavelmente irei fazer um rascunho em um branch esta semana para demonstrar).

(Além disso, estamos Mocha para que você possa require("mocha").it , mas a versão já estamos manter francamente é estranho, inconsistente , sujeito a bugs e nem mesmo evita poluir o namespace global. Se vamos manter esse tipo de recurso, podemos também corrigi- lo em vez de manter uma versão duvidosa.)

Isso é IMO em contraste com ser capaz de node <test file> para testes Mocha, que até onde eu sei não tem nenhuma vantagem e seria igualmente difícil de implementar dentro ou fora do Mocha.

@stevenvachon
como é que const {describe, it} = require('mocha') não está funcionando para mim? Acabei de ficar indefinido ...

$ node -v
v8.4.0

@zapphyre, você ainda precisa executar o conjunto de testes por meio do programa mocha , não por meio de node .

Você não pode executar vários arquivos de origem com node , então você está preso com mocha . Não conheço um executor / estrutura de teste que não tenha seu próprio executável.

Atualizado o título do problema para ser mais específico

Outra razão para isso: eu gostaria de executar testes usando um tempo de execução de JS diferente, por exemplo low.js (porque é uma de nossas plataformas, outras são node.js, navegador - e React Native). Posso fazer isso quando tenho um arquivo JS que importa o mocha e executa os testes, não posso fazer isso quando tenho que chamar mocha para executar os testes.

Quer dizer, você já faz isso pelo navegador. Nessa plataforma tenho JS código que as importações "mocha" e, em seguida, executa os testes de um global de mocha objeto usando mocha.run() .

No navegador eu

  • Carregue o mocha e obtenha um objeto mocha global
  • Execute mocha.setup({ui: 'bdd'});
  • Carregue todos os arquivos de teste usando SystemJS
  • Então eu corro
      mocha.checkLeaks();
      mocha.globals([]);
      mocha.run();

E é isso. Quando tento o mesmo em node.js, usando uma instrução require em vez de SystemJS para carregar um arquivo de teste, eu obtenho (usando mocha 5.2.0)

> mocha.run();
TypeError: Cannot read property 'search' of undefined
    at Mocha.mocha.run (/home/mha/one/node_modules/mocha/mocha.js:149:54)

EDIT: Eu tentei https://github.com/mochajs/mocha/wiki/Using-Mocha-programmatically e mocha.addFile vez de apenas carregá-lo, mesmo resultado.

Existe atualmente alguma maneira de importar o describe / it?

Há uma nova página Wiki (com 20 horas) https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically, mas é para executar os testes.

Outra razão para isso: eu gostaria de executar testes usando um tempo de execução de JS diferente, por exemplo low.js (porque é uma de nossas plataformas, outras são node.js, navegador - e React Native). Posso fazer isso quando tenho um arquivo JS que importa o mocha e executa os testes, não posso fazer isso quando tenho que chamar mocha para executar os testes.

Se o tempo de execução alternativo tiver um executável semelhante a um nó (o que eu acho que o low.js tem?), Você pode fazer alternative-executable ./node_modules/.bin/_mocha , e isso deve funcionar

Não conheço um executor / estrutura de teste que não tenha seu próprio executável.

https://github.com/tapjs/node-tap/#tutti -i-gusti-sono-gusti

Agora que https://github.com/mochajs/mocha/issues/3006 foi resolvido, seria bom ter as mesmas exportações com nome no navegador.

Atualmente, o seguinte está funcionando:

<script type="module">
  import './node_modules/mocha/mocha.js'
  console.log(mocha)
</script>

Mas o seguinte não é:

<script type="module">
  import { mocha } from './node_modules/mocha/mocha.js'
  console.log(mocha)
</script>

(conforme solicitado por https://github.com/mochajs/mocha/issues/956#issuecomment-167453800)

Isso pode se tornar mais fácil quando fundirmos nosso branch com roll-up e configurarmos os transpilers. Parece viável ter um pacote ESM e um pacote legado que consome os caminhos do código ESM e adiciona os vazamentos globais. Teríamos que tomar muito cuidado para fazer alterações apenas em caminhos e pacotes de código recém-adicionados, para não quebrar ou codificar tanto em navegadores quanto em nós

Com vars e tipificações globais, isso apenas torna o mocha legado.

A propósito, acabou de criar @types/mocha sem globais: https://github.com/whitecolor/mocha-types

Espero que eles (globais) sejam removidos oficialmente em breve.

Esta página foi útil?
0 / 5 - 0 avaliações