Mocha: this.timeout () falha ao usar as funções de seta do ES6

Criado em 21 dez. 2015  ·  59Comentários  ·  Fonte: mochajs/mocha

Ao usar Node> = 4 com "use strict" e sintaxe ES6 para funções de seta, o mocha falha:

describe('foo', () => {
  this.timeout(100);
});

# => TypeError: this.timeout is not a function

Usar a sintaxe ES5 funciona:

describe('foo', function() {
  this.timeout(100);
});

Então, que tipo de truque feio faz mocha com this ?

faq

Comentários muito úteis

Obrigado.

Por que tantas "mágicas" que, no final, geram problemas? Por que não isso ?:

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', () => ... );  
});

Sem globais, sem mágica problemática ... mas apenas JavaScript.

Todos 59 comentários

Ele vincula a função ao contexto de teste, o que não pode ser feito ao usar funções de seta. De http://mochajs.org/

screen shot 2015-12-21 at 8 06 34 am

Desculpe por isso!

Obrigado.

Por que tantas "mágicas" que, no final, geram problemas? Por que não isso ?:

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', () => ... );  
});

Sem globais, sem mágica problemática ... mas apenas JavaScript.

O que você está propondo é uma grande mudança de última hora e algo que está sendo discutido em https://github.com/mochajs/mocha/issues/1969#issuecomment-160925915 Uma reescrita pode introduzir esses tipos de semântica :)

Bom saber. Obrigado.

@ibc bem, teria que ser

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', (suite) => ... );  
});

mas os dois argumentos de suíte diferentes são do mesmo tipo? provavelmente não, então você tem nomes de variáveis ​​diferentes para refletir os diferentes tipos, como

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', (test) => ... );  
});

de qualquer forma, o operador de seta não pode ser usado em funções que esperam contextos.

Eu não imagino quebrar a interface do usuário do BDD para # 1969 - logo de cara, de qualquer maneira - embora eu pudesse ser persuadido. Esperava manter a API existente e apresentar um pacote separado contendo a IU do BDD. O Mocha será enviado com uma versão da IU do BDD que usa a API existente, mas depois podemos lançar uma nova versão do pacote da IU do BDD usando lambdas - os usuários podem escolher se querem ou não atualizar explicitamente para esse pacote.

Talvez a sintaxe ES6 alternativa para describe ou suite wrapper possa resolver este problema:

describe({ feature: 'create stuff' , do () {
    it('abc', () => {
    }); 
}})

Isso permitiria pelo menos a vinculação no nível da suíte.

alguma atualização disso? eu tenho isto

mocha = require('mocha');

mocha.describe('test', (suite) => {

suite.timeout(500);

suite.it('test', (done)=>
          )
    }
)

E obtendo TypeError: Não é possível ler a propriedade 'timeout' de indefinido

@mroien este não é um bug do Mocha. a sintaxe da seta não é uma substituição 1: 1 para function . por favor, leia sobre suas limitações

Aconteceu alguma coisa com isso? Gosto da solução proposta, mesmo que seja apenas por meu amor pelas funções das flechas e pela aversão a 'isso' quando não é necessário

Já que o tempo limite só é relevante com done , por que não simplesmente anexar a função de tempo limite à função concluída.

it('makes sense', done => {
    done.timeout(100);
});

@nomilous isso ainda não funciona. Eu tive um problema semelhante. O que funciona para o meu caso é chamar setTimeout dentro do bloco it . por exemplo

it('description', done => {
     const func = () => {
          // assertions
     };
     setTimeout(func, 10000);
});

@nomilous synchronous ou casos que retornam uma promessa também podem ter um tempo limite.

@ andela-engmkwalusimbi isso não deveria funcionar. Como @boneskull escreveu :

@mroien este não é um bug do Mocha. a sintaxe da seta não é uma substituição 1: 1 para a função. por favor, leia sobre suas limitações

Para todos que ainda estão se perguntando sobre isso, certifique-se de entender o que a função de uma seta implica e, em seguida, volte aqui e continue lendo (há muitos recursos por aí que podem explicar isso muito melhor do que eu).

A única maneira dessa função de seta funcionar neste caso é se alterarmos a API bdd para passar um objeto context para cada retorno de chamada do Runnable (ganchos, testes), em vez de aproveitar this . Isso não é uma ideia, mas é um terremoto de uma mudança radical, então nunca vai acontecer. Em vez disso:

it('should do something', function (done) {
  this.timeout(9000);
  // stuff
  done();
});

ficaria assim:

it('should do something', (context, done) => {
  context.timeout(9000);
  done();
});

Isso quebraria todos os testes assíncronos do Mocha existentes, independentemente se fosse compatível com versões anteriores:

it('should do something', function (context, done) {
  // context is unused, but 'done' is now the second parameter
  this.timeout(9000);
  done();
});

Poderíamos fornecer uma implementação alternativa bdd que faz isso, entretanto - não seria o padrão.

Essa é a explicação mais completa que tenho de "onde está esse problema". :sorriso:

Talvez pudesse ser levado em consideração para a próxima versão principal? Não acho que seja uma mudança importante o suficiente para criar uma implementação alternativa de bdd. Ter nomes de argumentos pode ajudar no desenvolvimento futuro também e talvez criar uma maneira simples de adicionar algum tipo de middleware de teste como:

it('should do something', function ({ context, done }) { ...

Poderíamos fornecer uma implementação alternativa de bdd que faça isso, no entanto - não seria o padrão.

@boneskull Uma nova interface bdd-es6 seria ótimo :)

Embora eu ame as funções de seta que são realmente úteis para, por exemplo, funções de Array como .filter(i => i.val) , qual é o problema em usar funções normais? Eu acho que é muito útil ter uma descrição global, então eu não tenho que exigir isso todas as vezes. Além disso, desde quando this mágico, só porque você não entende as funções de (seta)? Definitivamente, não quero fornecer uma variável sempre que puder retornar promessas, caso contrário, eu teria mudado para algo como ava há muito tempo. Em relação à simplicidade do mocha, acho que não deve haver nenhuma grande mudança nas funções normais / seta descritas em # 1969. E, por favor, não me diga que as funções de seta são mais rápidas de digitar, já que seu editor pode transformar um único f em function () {\n\t\n} .

não estou certo, existe uma solução para o tempo limite de uma chamada before() que usa funções de seta?

    before( async function () {
      data = await provider.getData();
      console.log(data);
      this.timeout(30*1000);
    });

Não tem efeito. ainda tendo um tempo limite de 4 segundos aqui. Esta é a única coisa lenta em meu conjunto de testes. Posso colocar o tempo limite de 30s em mocha.opts para resolver o problema, mas realmente não preciso que todos os testes atinjam o tempo limite após 30s, apenas uma chamada de API quando 4s é bom para 99% deles.

Aqui está como eu resolvi isso nesse meio tempo (observe que o primeiro describe() usa function vez da sintaxe de seta grossa:

describe('Search API', function () {
    this.timeout(30*1000);

    context('search', () => {
        let data;

        before( async () => {
          data = await provider.getSearch();
        });

        it('returns results', () => {
          expect(data).to.exist;
          expect(data.results).to.be.instanceOf(Array);
          expect(data.results.length).to.be.above(0);
        });
    })
});

@chovy Você está definindo o tempo limite após o tempo limite ocorrer em await provider.getData() .
Em vez disso, tente usar:

before(async function () {
  this.timeout(30*1000); // set timeout first, then run the function
  data = await provider.getData();
  console.log(data);
});

não estou certo, há uma solução para o tempo limite de uma chamada before () que usa funções de seta?

Só para ficar claro: atualmente não há como chamar timeout do Mocha usando as funções de seta. Qualquer discussão sobre alternativas (por mais meritórias que sejam) é uma discussão sobre possíveis interfaces novas (ou pelo menos modificadas).

Algo que tenho em mente há algum tempo era capaz de fazer:

it('...', (done) => {
  ...
  done()
})

e

it('...', (t) => {
  t.timeout(500)
  t.tag('integration', 'api')
  ...
  t.done()
})

usando a mesma interface padrão.

O suporte a ambos na mesma interface padrão permite que você comece a usar as funções de seta em uma base de código existente. Vale ressaltar que a sintaxe (done) encontrada em muitos tutoriais online ainda funcionaria, sem sinalizadores nem nada.

Portanto, nesta implementação, você obtém como parâmetro a função tradicional done , mas com as funções de utilidade adicionadas como propriedades desse objeto de função done .

usar this.timeout() faz com que o tempo decorrido desapareça do relatório.

@dasilvacontin não

@dasilvacontin oh, eu me lembro. porque você tem que chamá-lo. você pode não querer.

Desculpe, você pode entrar em detalhes sobre "tenho que ligar", @boneskull? Você está falando sobre os problemas em que Mocha pensará que o teste é assíncrono?

Em 29 de janeiro de 2017, às 05:54, Christopher Hiller [email protected] escreveu:

@dasilvacontin oh, eu me lembro. porque você tem que chamá-lo. você pode não querer.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

Além disso, como você declara sua intenção de fazer um teste assíncrono ao usar 't'?

Em fita, você sempre deve chamar 't.done' ('t.end' em sua API) ou definir a quantidade esperada de asserções ('t.plan').

Seria possível adicionar um terceiro argumento a ele () com opções? Isso não quebraria a API.

it ('accepts lambda', (done)=>{ doWork();  done(); }, {timeout:60000});

@boneskull

E se, em vez de colocar o contexto em primeiro lugar, fosse um segundo opcional?

Em vez disso:

it('should do something', function (done) {
  this.timeout(9000);
  // stuff
  done();
});

ficaria assim:

it('should do something', (done, context) => {
  context.timeout(9000);
  done();
});
it('should do something', function (done, context) {
  // context is unused, but 'done' is now the second parameter
  this.timeout(9000);
  done();
});

Poderíamos fornecer uma implementação alternativa de bdd que faça isso, no entanto - não seria o padrão.

Essa é a explicação mais completa que tenho de "onde está esse problema". 😄

ou se o contexto tivesse que ser algum parâmetro definido

it('should do something', function (done, override) {
  // context is unused, but 'done' is now the second parameter
  override.timeout(9000);
  done();
});

Mas eu pegaria uma interface não padrão também :)

A desvantagem de qualquer solução que requeira done é que você tem que usar done mesmo se retornar uma promessa fosse mais simples. Falando por mim mesmo, sei que prefiro digitar function e return que .then(()=>{done()}, done) !

@Flamenco Essa é uma ideia interessante, embora eu possa preferir que a função por último (sim, é tecnicamente mais "mágico" dessa forma, mas, vamos enfrentá-lo, é fácil verificar se o parâmetro é uma função e se é intuitivo o suficiente para usar e não é como se Mocha estivesse sendo alimentado em algum tipo de outra abstração que tal "mágica" poderia quebrar); tal objeto de opções também pode ser usado para fornecer tags e / ou motivos de teste pendente, ambos os quais são recursos solicitados (encontrá-los na caixa de pesquisa de problemas do GitHub / lista de RP é deixada como um exercício para o leitor) com os quais eu ' Vou admitir que me solidarizo (mesmo que seja possível contornar sua ausência, há vantagens na coisa real sobre as soluções alternativas).

@ScottFreeCode Eu definitivamente concordo sobre done Eu também não gosto de usar isso, eu estava apenas comentando sobre a necessidade de colocar o contexto primeiro vs último e usando o exemplo mencionado acima fornecido por boneskull.

Eu adoraria a opção de passar no contexto ou definir algum interruptor no mocha para execução de es6.

Enquanto isso, estou apenas resumindo aquelas poucas descrições de teste que requerem diferenças de tempo limite em uma chamada de função () de aparência derpy.

Eu encontrei uma solução alternativa para o caso de uso em que uma promessa é retornada ao mocha a partir de uma função de seta que precisa de um tempo limite personalizado, que desejo compartilhar no caso de outros acharem útil.

Adicionando a seguinte função when definida abaixo em uma declaração de teste como: it('does something', when(() => someFunctionReturningAPromise, customTimeout)) o tempo limite do mocha pode ser definido sem desistir das funções de seta.

Da mesma forma, e porque estamos trabalhando apenas com promessas, podemos refazer o primeiro parâmetro para um teste e passar no contexto em vez do retorno de chamada concluído: it('can access the mocha test context', when(testContext => someFunctionThatNeedsContext(testContext))

const when = (lambda, timeout) =>
  function() { // return a plain function for mocha to bind this to
    this.timeout(timeout || this.timeout() || 1000)
    return lambda(this)
  }

Alguns casos de teste para ilustrar:

const delay = timeout =>
  new Promise((resolve, reject) => setTimeout(resolve, timeout))

const deject = timeout => // similar to above, but a delayed reject
  new Promise((resolve, reject) => setTimeout(reject, timeout))

describe('mocha testing', () => {
  context('with only arrow functions', () => {
    context('tests that do not time out', () => {
      it('passes fast', when(() => delay(10), 100))
      it('does not usually time out', when(() => delay(2000), 2010))
    })
    context('tests that will time out', () => { // these should fail if the 'when' function works properly
      it('times out fast', when(() => delay(1000), 10)) // will fail in 10ms
      it('times out', when(() => delay(1000), 1000)) // will fail in about 1000ms
    })
    context('tests that will reject', () => { // this shows the when function works as expected when a test rejects
      it('fails fast', when(() => deject(10), 100))
    })
  })
})

@ astitt-ripple sim, ou simplesmente escreva function () {} ... Wtf?

Ok luca, mordida mal. :-)

A diferença com a função de seta é que o retorno pode ser ignorado. Em um
configuração de tipo es6, isso já pode ser um padrão comum para promessa 'então'
correntes.

Com um bloco de funções, como você sugere, e até onde eu sei, o retorno para
a promessa deve ser explícita. No mínimo, para uma função de teste baseada em promessa
{...} está errado, um teste deve sempre retornar sua promessa, então o mínimo
A trivialização válida é na verdade: function {return ...}. Caso contrário, o
o teste retorna indefinido para mocha e não a promessa despachada ... e o
o autor do teste está passando por maus bocados.

Se a maior parte do código em uma base de código já for funções de seta de promessa.
e / ou um estilo de programação funcional, adicionar retorno de função pode parecer
inconsistente. O formulário 'quando' sugerido está disponível para aqueles que
prefere setas e um estilo mais funcional do que a função tradicional
estilos de retorno de chamada ou de retorno de função. É mais conciso e se encaixa com o
describe o contexto dsl todos concordamos que gostamos de escrever testes, levando em
conta a promessa de abstração de programação assíncrona que o javascript manipula para
Nós vamos. Além disso, é mais curto do que function + return, mesmo sem nosso amigo curly:
quando (() => ...).

Talvez esse não seja o estilo preferido deste projeto, eu entendo isso, e
Eu não estou propondo isso como uma mudança para o projeto. Talvez seja mesmo
repreensível como seu wtf implica. Isso é bom. Mocha tem que trabalhar com
js amigável pré-fp. A compatibilidade com versões anteriores é uma preocupação de primeira classe. Este
também faz sentido.

Este é um thread longo e bloqueado, e esta é uma saída. Uma agradável
a coisa sobre o javascript é que não precisa haver uma maneira ou um estilo para
fazer as coisas. Não temos que concordar em estilo. Funcionalmente
falando, meu post anterior dá às pessoas uma maneira de usar flechas e promessas
de forma consistente e com o teste dsl, sem abrir mão do acesso ao mocha
contexto, de uma forma amigável de programação limpa e funcional que não tinha
foi sugerido anteriormente.

Obrigado.

Se a maior parte do código em uma base de código já é ... estilo de programação funcional ...

... então usar this.mutatingMethod(currentConfiguration) para definir comportamentos, especialmente de uma função (ou melhor, sub-rotina) que já está sendo executada, é muito mais inconsistente do que ter que escrever return (que é apenas sintaxe) , e a aparência inconsistente tornaria essa realidade mais óbvia, em vez de realmente introduzir a inconsistência.

(Não me interpretem mal, estou muito feliz com o aumento da popularidade das idéias de programação funcional em JS; mas a sintaxe de function / return vs => é não é realmente essencial para a programação funcional, apenas uma questão do que parece mais limpo nesse paradigma, enquanto outros conceitos como pureza e declarativo vs. imperativo são realmente essenciais. Seria legal ter um executor de teste que fosse, em vez disso, mais funcional em seu comportamento / semântica , ponto em que alternar para funções de seta provavelmente seria trivial ...)

Eu concordo com você que this.timeout é mutacional e quebra o paradigma de programação funcional. Claro que é totalmente inconsistente. No momento, há alguma outra maneira de declarar um tempo limite personalizado por teste além de this.timeout?

Na implementação atual do mocha, parece necessário escapar de volta ao paradigma imperativo / mutacional ou desistir da configuração por tempo limite de teste. Um ou outro.

Na abstração when , um tempo limite é o segundo parâmetro para quando, permitindo que um estilo funcional permaneça no nível do teste. Ele esconde essa fuga inevitável do modo FP de volta à programação imperativa, mas o faz em um só lugar. Além disso, dá às funções de seta acesso ao contexto mocha, que de outra forma não seria possível sem quebrar a convenção do parâmetro da função de teste. Portanto, ele potencialmente resolve alguns problemas para alguns dos usuários deste projeto (a julgar pelo histórico deste problema) no ínterim até que alguém explore sua ideia de executor de teste.

Também, por favor, não me entenda mal. Eu não acho que a programação funcional jamais irá ou deva substituir completamente a programação imperativa / mutacional derivada da máquina de turing. Por exemplo, em quase todos os casos, em um tempo de execução JS, o código, funcional ou não, é interpretado por um programa escrito naquele estilo imperativo mais tradicional (provavelmente c ++, mas não necessariamente), rodando em um sistema operacional também escrito em torno de mutacional e ideias imperativas (provavelmente C). A memória é um recurso fixo, estruturas de dados imutáveis ​​são uma mentira que o tempo de execução nos conta. Esse modelo fundamentalmente imperativo é o padrão em computação e veio para ficar. Mas isso não significa que a programação funcional não possa coexistir em cima dele. E se for assim, é inevitável que o código do PF tenha que cair no modelo subjacente de vez em quando. Não acho que isso deva significar que devemos levantar as mãos e dizer que é toda a sintaxe, e vamos apenas usar função / retorno.

Na verdade, podemos fazer programação funcional em C dada uma certa tolerância para detalhes, assim como é verdade que você pode fazer programação funcional com função / retorno em vez de => funções. Só não se esqueça de retornar sua promessa. FP em C exige um pouco mais de digitação, o que, afinal, é apenas mera sintaxe ... / s

No final do dia, as funções de seta nos aproximam cada vez mais de uma maneira viável e prática de trabalhar no modelo de cálculo lambda, em uma linguagem popular. Remover esses caracteres extras faz uma diferença pequena, mas importante. Ainda existem muitas limitações práticas, mas muitas delas são solucionáveis, o assunto em questão é uma delas.

De qualquer forma. Vou usar minha função auxiliar sugerida, e outros estão livres para usá-la agora. Estou ansioso por sua solução de executor de teste, mas enquanto isso, não sei se será particularmente produtivo continuar tentando convencer um ao outro de nossas próprias opiniões sobre qual sintaxe é importante ou não, como se houvesse é uma maneira. Eu gosto de setas e você gosta de funções. Não vi um argumento convincente que mude minha visão. Estou aberto a um, mas é melhor que seja mais pensado do que reflexos como: "apenas use a função, wtf" ou "você ainda pode fazer fp com uma _sintaxe_ mais prolixa".

Resolva o problema de outra maneira e ninguém precisará usar when . Disse Nuff. :-)

Não acho que isso deva significar que devemos levantar as mãos e dizer que é toda a sintaxe, e vamos apenas usar função / retorno.

Eu gosto de setas e você gosta de funções.

... é melhor que seja mais pensado do que reflexos como: ... "você ainda pode fazer fp com uma sintaxe mais detalhada".

No entanto, isso é praticamente o oposto do que eu disse - eu estava indo para o fato de que as funções de seta que ainda usam this não seriam FP em primeiro lugar, elas seriam OO com funções de seta ( o inverso do FP com funções JS tradicionais). Em outras palavras, longe de ser apenas uma sintaxe diferente, o problema real é que há uma incompatibilidade de paradigma mais profunda do que apenas a incompatibilidade de sintaxe (como o Mocha é projetado atualmente).

Tenho quase certeza de que é possível construir uma interface alternativa em cima do Mocha para substituir this completamente por parâmetros. Eu só quero deixar claro que se você quiser escrever testes de FP, você terá que fazer mais do que apenas encontrar uma maneira de passar this do Mocha para as funções de seta. Estou muito para subir para esse tipo de desafio, no entanto. ; ^)

(Existem também vários outros comportamentos do Mocha que são com estado, globais ou - pior - ambos, mas não tenho tempo no momento para pensar em uma maneira resumida de listá-los. Se você já viu os problemas relacionados à execução do Mocha mais de uma vez, este é um exemplo deles.)

No momento, há alguma outra maneira de declarar um tempo limite personalizado por teste além de this.timeout?

Infelizmente, tenho quase certeza de que não; da minha cabeça, a sugestão de aceitar um parâmetro adicional para it que seria um mapa de valor-chave (como um objeto JS) das configurações soa como uma solução futura decente no Mocha, se alguém quiser tente implementá-lo.

No momento, há alguma outra maneira de declarar um tempo limite personalizado por teste além de this.timeout?

Infelizmente, tenho quase certeza de que não;

Obrigado por confirmar este detalhe. Isso solidifica o que eu estava defendendo.

de repente, a sugestão de aceitar um parâmetro adicional que seria um mapa de valor-chave (como um objeto JS) de definições de configuração soa como uma solução futura decente no Mocha, se alguém quiser tentar implementá-la.

1 para uma solução mais geral.

Pelo que posso dizer, porém, parece viável passar um único parâmetro ainda. Combine this com o retorno de chamada done (então this se torna uma função). Em seguida, faça com que o mocha execute cada teste embrulhado com uma promessa (bem, dois na verdade, um para lidar com o tempo limite e um para realmente executar o teste), independentemente da contagem do parâmetro (diferente de como funciona hoje). Ele poderia então verificar se o resultado da função era uma promessa ou não. Caso contrário, chame done depois que a função síncrona retornar para finalizar o teste. Se o resultado da função for, em vez disso, uma promessa, aguarde a resolução (ou rejeite). Se o tempo limite ocorrer, pare o teste (o mesmo de antes). No caso em que um teste consegue chamar done e também retornar uma promessa. Qualquer um dos done é chamado antes da resolução; nesse caso, mocha deve esperar pela promessa e, em seguida, falhar no teste por ter uma sequência de conclusão ambígua. Ou done é chamado algum tempo após a resolução, caso em que, de alguma forma, o teste deve ser reprovado retroativamente - ou o problema sinalizou de alguma outra forma razoável. Desculpe, isso é um monte de golpes gerais, mas esse é o meu entendimento de forasteiro sobre o que o mocha está tentando fazer e as peculiaridades que está enfrentando. Que outras considerações podem impedir que isso seja uma solução viável?

se você quiser fazer FP ao escrever testes, você terá que fazer mais do que apenas encontrar uma maneira de passar isso do Mocha para as funções de seta

concordou. há uma mudança definitiva em ir para um modelo computacional diferente e, além disso, o javascript é um ecossistema complexo com muito a considerar. No meu caso de uso específico, no entanto, definir o tempo limite de vez em quando (como para ser mais preciso com base em alguns cálculos em vez de um valor padrão fixo), é o único problema tangível que encontrei ao escrever testes FP com Mocha (até agora pelo menos). O que é ótimo. : +1:

Dito isso, eu gostaria de saber o que mais você vê como obstáculos iminentes que têm a ver especificamente com o Mocha (ao contrário do que escrever testes em FP pode significar em geral).

Isso é praticamente o oposto do que eu disse, embora

Lamento se descaracterizei ou entendi mal. Para muito do que escrevi, não tenho certeza se estava conseguindo o que pretendia, com base nas respostas. O que é lamentável, já que acho que se chegarmos ao fundo, provavelmente chegaríamos a um acordo bastante próximo sobre como o FP _deve_ ser na _teoria_. Aqui, no entanto, parece que discordamos sobre como uma redução viável para _prática_ pode parecer nas versões atualmente disponíveis do Mocha hoje, pelo menos para alguns usuários / casos de uso. Portanto, não tenho certeza de qual é exatamente o principal problema com a função add-on proposta que propus, de sua parte.

(Citado e respondido em ordem, mas sem dúvida o mais importante é mais tarde do que antes.)


Pelo que posso dizer, porém, parece viável passar um único parâmetro ainda. Combine isso com o retorno de chamada concluído (para que se torne uma função). Então ... Que outras considerações podem impedir que isso seja uma solução viável?

Se quebrarmos a compatibilidade com versões anteriores, podemos mudar para designs mais simples.

Se mantivermos a compatibilidade com versões anteriores, esses dois testes precisam ser aprovados e não expirar:

it("runs immediately", () => {
  // call functions and assert whatever
})
it("runs asynchronously", doneWithSomeOtherName => {
  setTimeout(() => {
    // call functions and assert whatever
    doneWithSomeOtherName()
  }, 100)
})

Você é bem-vindo para tentar criar algum código de exemplo para provar o contrário (embora eu sugira focar na sugestão no final deste comentário), mas tenho quase certeza de que nenhum design será capaz de fazer isso e também fazer este teste passar e não expirar:

it("looks just like an asynchronous test with a different name for `done`, but never tells Mocha it's done", context => {
  context.configureByMutation("some value")
  // call functions and assert whatever
})

Mas também, observe a mutação aí. Mais sobre isso abaixo.


No meu caso de uso específico, no entanto, definir o tempo limite de vez em quando (como para ser mais preciso com base em alguns cálculos em vez de um valor padrão fixo), é o único problema tangível que encontrei ao escrever testes FP com Mocha (até agora pelo menos). O que é ótimo.

: +1 :!


Dito isso, eu gostaria de saber o que mais você vê como obstáculos iminentes que têm a ver especificamente com o Mocha (ao contrário do que escrever testes em FP pode significar em geral).

Acho que posso não ter comunicado isso com bastante foco ... edamente, então deixe-me ver se consigo resumir um pouco mais. (Também sinto muito se alguma coisa tiver saído como antagônica; certamente não é para ser, embora eu admita que tentei mudar a mentalidade aqui.) A base de código do Mocha é muito das hierarquias de classes e do estilo getters e setters de "orientação a objetos" (e há vários problemas ou problemas em potencial que o Mocha tem que eu acredito ter chegado ao seu estado mutável), mas isso geralmente não afeta o seu código de teste se tudo o que você está fazendo é escrever testes e permitir que o Mocha os execute . Você pode fazer coisas estranhas com a configuração mutante imperativa do Mocha:

it("imperatively sets the timeout multiple times", function(done) {
  this.timeout(5000)
  var context = this
  setTimeout(function() {
    context.timeout(1000)
    setTimeout(done, 500)
  }, 4000)
})

... mas você não precisa. Como acontece com tantos elementos de programação funcionalmente em linguagens não funcionais: apenas não abuse das coisas imperativas.

(Há também um argumento a ser feito de que as exceções são impuras, mas ainda não estou convencido de que isso seja verdade para as exceções que são lançadas com base na entrada - o que poderia ser considerado apenas outra forma de saída. Portanto, alguns diriam que usar asserções esse lançamento não é funcional, mas não vou abordar isso neste momento.)

(Parte importante aqui :) O que estou tentando chegar é que estamos olhando para potencialmente adicionar complicação a uma base de código já complexa ou fazer uma alteração incompatível com versões anteriores. Precisamos de justificativa para qualquer uma dessas coisas. Se a justificativa for "tornar os testes mais funcionais", isso é bom (pelo menos no meu livro). Um design que torne os testes mais funcionais pode valer a pena (dependendo de quantos problemas ele representa). Mas se por "tornar os testes mais funcionais" você quer dizer "tornar as funções das setas mutantes", isto é, "tornar as funções das setas menos funcionais", isso enfraquece muito o caso (se não for apenas contraditório). Mais completamente: eu não acho que fazer os testes parecerem mais funcionais (tão simples quanto as funções de seta parecem!) Enquanto retém um pouco de mutação envolvida, por menor que seja essa parte, seja uma justificativa quase tão convincente quanto realmente livrar-se dessa pequena mutação seria - pelo menos se o objetivo for tornar os testes mais funcionais.

Eu provavelmente não deveria nem ter saído tão longe nessa tangente; veja abaixo sobre as soluções. 😸


Portanto, não tenho certeza de qual é exatamente o principal problema com a função add-on proposta que propus, de sua parte.

(Também parte importante aqui :) Bem, eu gosto da parte em que usa timeout como um parâmetro em vez de uma chamada de método, na verdade! Se você puder encontrar uma maneira de generalizar isso para o resto dos métodos de configuração do Mocha (há muitos deles - e alguns se aplicam a testes síncronos, se bem me lembro correto, por isso não podemos simplesmente adicionar os mesmos métodos que propriedades em done e permitir que as pessoas escrevam testes assíncronos que podem chamar a configuração por meio de done , mas estou divagando), então eu definitivamente gostaria de dar uma olhada nisso. No mínimo, podemos querer fazer uma recomendação e podemos até mesmo ser capazes de adotar a implementação em it quando passado um terceiro parâmetro (ou algo parecido, talvez it.configured ou it(...).configured(...) se não quisermos mais travessuras de número de parâmetros ...) - o que eu acho que seria uma solução compatível com versões anteriores que aborda a mutação subjacente / questão imperativa e obtém suporte da função de seta "a direita maneira "(o que estou argumentando é assim mesmo): porque se encaixa no novo comportamento. Eu acho que o que eu deveria ter dito, em vez de ir atrás de this na solução alternativa, é: vamos expandir a parte do parâmetro disso!

Juro que li em algum lugar que você poderia fazer algo assim:

describe('a slow thing', () => {
 // test code...
}).timeout(5000);

que não altera o contrato de parâmetro para a função fornecida. Agora não consigo encontrar nenhuma referência a tal coisa, então talvez eu apenas tenha imaginado.

@ thom-nic, isso funciona! Acho que faz sentido, já que todas as funções do mocha retornam seu contexto

return this;

Funciona comigo ao substituir a forma da função de seta pela função normal

função () {.....}

Oi pessoal. Peço desculpas por ter escurecido depois de iniciar a discussão em agosto, estive bastante ocupado e, na verdade, quase concluí / segui em frente com esse trabalho.

Agradeço a resposta detalhada sobre os diferentes casos de uso e como é difícil combiná-los. Essa foi a exibição mais concisa das diferentes configurações que o mocha tem para suportar que eu li. Então, obrigado pelo seu tempo nisso.

Olhando para trás, é claro que devo ter enfatizado demais o acesso ao contexto mocha ( this ), quando esse aspecto era realmente mais um pensamento posterior conveniente. Eu não percebi como facilmente desviaria a atenção do que eu estava realmente tentando fazer: o que era me divertir adicionando uma extensão temporal'ish ( when ) ao dsl de teste para simplificar uma única vez ajustes de tempo limite (além de eliminar um erro comum para um estilo específico de testes, que explicarei abaixo). Devolver this foi apenas outra coisa divertida que pensei em adicionar, a ideia principal (daí o nome when ) era lidar com casos que precisavam de tempos limites diferentes do normal.

Obviamente, se eu quisesse acessar o contexto vinculado, poderia simplesmente usar function diretamente como muitos sugeriram, em vez de içá-lo com um invólucro. Esse não é o problema. 😄 Não esqueci como isso pode parecer estranho à primeira vista. Espero que talvez isso melhore o quadro se eu mostrar como estava preparando alguns dos testes que me conduziram por esse caminho. Para ser claro, não estou tentando vender nenhum estilo em particular aqui, use o que funciona para você. Isto é o que funcionou para mim.

OK
Primeiro, comece com a suposição de que estamos testando alguma configuração que basicamente faz uma coisa, mas fará para uma ampla gama de entradas e, portanto, devemos testar isso em vários cenários para garantir que as saídas estejam corretas . No entanto, como o código do aplicativo relevante "faz uma coisa", o procedimento de teste subjacente é quase sempre o mesmo. Também não quero duplicar / modificar o corpo de teste desnecessariamente, já que isso diminui a rapidez com que posso adicionar mais casos de teste para novas entradas e, eventualmente, a manutenção se tornaria irracional.

Então, em vez disso, escrevemos uma função que é geral o suficiente para iniciar o código do aplicativo com quaisquer entradas potencialmente suportadas, realizar a ação de teste e, em seguida, declarar os resultados ... Adicione que no meu caso eu estava trabalhando com a abstração Promise (por razões que vou deixar de fora), portanto, essa função de procedimento de teste generalizado naturalmente tem que retornar essa cadeia promessa. Pão e manteiga são6 tipo de coisa, até agora tudo bem.

Agora vêm os cenários de teste, uma vez que empacotamos tudo em nossa função de procedimento de teste, os casos de teste estão efetivamente definindo as entradas e invocando a função.

Então, talvez eu escreva um monte de testes como este, e tudo _parece_ estar funcionando:

it('does something', function() {
  testProcedureFunction('something','1')
})

Se você estiver acompanhando de perto, provavelmente já deve ter notado que este exemplo tem um bug. Está faltando return e, como testProcedureFunction é baseado em promessas (trocadilho intencional), sempre vai passar, não importa se as afirmações no final são aprovadas ou reprovadas. Este é um bug, e às vezes pode ser extremamente sutil para rastreá-lo. Para ilustrar, dependendo de como escrevemos testProcedureFunction e como o aplicativo é escrito, digamos que haja algum código síncrono no início e que exploda em vez das afirmações de fim de teste, o caso de teste pode até falhar - nos levando pensar que está tudo bem.

O teste deve ser realmente parecido com isto, com um retorno:

it('does something', function() {
  return testProcedureFunction('something','1')
})

Agora eu sei que este teste será de uma linha na maioria dos casos. Na verdade, cada caso será uma linha, exceto quando as entradas forem tais que um tempo limite maior seja necessário. Agora, entre as diferenças entre as funções js clássicas e setas, há um aspecto particular das funções de seta que é útil aqui: uma única função de seta de instrução tem um retorno implícito quando as chaves são omitidas. Se, em vez de escrever function {...} , um teste usar => ... , então posso facilmente examinar esses casos em busca da seta e da falta de chaves e inferir rapidamente que eles não podem ter esse return faltando

Igual a:

it('does something', () => testProcedureFunction('something','1'))

E se um desses casos demorar mais do que os outros! Podemos, é claro, definir o tempo limite assim:

it('does something slow', function() {
  this.timeout(10000)
  return testProcedureFunction('somethingSlow','2')
})

Ou talvez alguém cometa um erro e faça isso primeiro (o que não funciona, é claro):

it('does something slow', () => {
  this.timeout(10000)
  return testProcedureFunction('somethingSlow','2')
})

Mas agora estamos de volta onde começamos, a base de código tem um padrão maduro para repetição, que é suscetível ao bug de retorno ausente (seja por mim no futuro ou pela próxima pessoa a adicionar um recurso - o ponto é, é fácil erro de cometer, pode passar despercebido e pode ser difícil de rastrear). A função when resolve isso e nos permite usar as setas de forma consistente novamente:

it('does something slow', when(() => testProcedureFunction('somethingSlow','2'), 10000))

(note, eu não consegui fazer a sugestão de encadeamento de pontos .timeout(5000) acima funcionar, pode ter sido devido à versão do mocha que eu precisava usar, não me lembro mais, darei a isso um Experimente!)
(nota 2, observe que o uso de when não está usando o truque de içamento de parâmetro this - realmente foi apenas um pensamento posterior).

Talvez haja linters que podem sinalizar bugs de retorno para promessa ausentes (ou, provavelmente, de forma mais realista, impondo uma instrução de retorno com um rhs para cada função). No entanto, essa não era uma opção na época, além disso, acho que a sintaxe da seta fica mais curta e acho (subjetivamente / pessoalmente) mais fácil de ler e trabalhar, o que me afastou de function .

Então aí está.

Não sei se terei tempo de responder novamente em breve, então espero que tenha sido pelo menos informativo e claro, e talvez até coloque parte da controvérsia em torno de todo o "acesso ao contexto mocha a partir de setas" para cama.

Por último, como achei a função vs => confusa por um longo tempo, deixarei de lado este link caso não esteja claro para alguém que esteja lendo por acaso por que as setas não podem acessar this . Foi a explicação mais clara de funções vs flechas que encontrei e foi o que me ajudou a finalmente entender as diferenças bem o suficiente para usá-las com total confiança.

https://hacks.mozilla.org/2015/06/es6-in-depth-arrow-functions/

@ thom-nic Funciona em it , mas não em describe .

describe('my test suite', () => {

  it('test case 1', () => {
    // ...
  }).timeout('10s');  // Works on `it`. This test case doesn't timeout

  it('test case 2', () => {
    // ...
  });  // This test case timeouts.

}).timeout('10s');  // Doesn't work on `describe`. Tests are already created when runs to here.

@ thom-nic, você pode usar a forma de função normal

describe('my test suite', function() {
this.timeout(n);

...
}

Qualquer pessoa reclamando sobre isso não entende as funções das setas.

As funções de seta NÃO são uma novidade. O ES6 deve substituir o clássico function () {} . O único propósito das funções de seta é que ele herda this de seu pai, onde o clássico function () tem seu próprio this .

Sim, mesmo ao usar a sintaxe ES6 completa, você ainda deve usar function () se quiser usar this no contexto correto de sua função. Você deve usar function () e () => em seu aplicativo ES6, dependendo do que está tentando fazer.

this.timeout() não funciona com it('....', () => { ... }) porque o retorno de chamada está herdando this da função pai describe() , na qual this.timeout() não faz sentido nesse nível.

As funções lambda também não permitem que você envie automaticamente um único argumento para a função sem declará-lo na chamada?

(param) => aFunção
... então (aFunção)

function () {} pode ser associado a 'this' mas () => está 'bloqueado'

As funções de seta devem substituir a função tradicional quando o receptor espera um 'this' predeterminado no mesmo contexto de onde é chamado (e também se beneficia de digitar menos código).

Eu iria mais longe e diria nunca use function () a menos que você queira que 'this' seja algo diferente do que 'this' é ao invocá-lo.

@Flamenco...

As funções lambda também não permitem que você envie automaticamente um único argumento para a função sem declará-lo na chamada?

Não tenho certeza se entendi _exatamente_ como você está colocando isso.
No que diz respeito a "enviar argumentos para a função", as setas grandes funcionam como funções regulares, com uma única exceção: se você tiver exatamente 1 argumento, pode deixar de fora o parêntese.

() => console.log("hi"); //zero arguments requires empty parenthesis
a => console.log(a); //you can optionally leave the parenthesis off for 1 argument
(a,b) => console.log(`${a} ${b}`); //2..n arguments requires parenthesis

O que você pode ter chegado é que setas grossas permitem que você _retorne_ um valor omitindo as chaves e a palavra-chave return , desde que sua função seja uma expressão única.
Então, se você tivesse ...

setTimeout(function(a,b) { doSomething(); return calculateSomething(a,b); }, 5000);

... e você quisesse converter isso em uma função de seta grande, você não seria capaz de mexer as chaves e a palavra-chave return porque o corpo da função tem várias instruções. Você faria assim ...

setTimeout((a,b) => { doSomething(); return calculateSomething(a,b); }, 5000);

Se, em vez disso, você começou com ...

setTimeout(function(a,b) { return calculateSomething(a,b); }, 5000);

... então você está lidando com uma função tão simples que retorna apenas uma única expressão e você pode usar ...

setTimeout((a,b) => calculateSomething(a,b), 5000);

Isso ficou muito mais fácil de ler!
Escrevi um pouco mais sobre isso em codefoster.com/levelup-arrays .

Existem muitos estilos de codificação diferentes em JavaScript - de OOP a FP, de segurança de tipo estrita à tipagem mixin / duck. Além disso, existem padrões avançados em cada um desses estilos (ou seja, injeção de dependência no campo OOP, currying / mônada no campo FP).

Se o seu estilo de codificação estiver mais próximo do FP, onde this não é usado e as funções de seta são usadas para reduzir o clichê, ter que preservar this é uma sobrecarga extra para qualquer teste avançado (por exemplo, teste parametrizado, criação DSL).

Qualquer desenvolvedor experiente pode apenas pré-embalar a estrutura de teste para se adequar ao seu estilo de codificação, mas isso significa que a estrutura é menos "out-of-the-box". Isso se traduz em trabalho extra para atualização, adoção de plug-ins e integração de novos engenheiros.

Gosto da ideia de uma interface bdd alternativa que não use this e, em vez disso, passe o que normalmente seria o objeto de contexto como um parâmetro para describe , it e ganchos.

Mas não é tão simples de implementar, IIRC. Seria legal ver uma tentativa, no entanto.

Eu sei que isso está entrando em sérios efeitos colaterais, mas você não poderia lidar com o parâmetro feito ou contexto algo assim?

it("runs immediately", () => {
  // call functions and assert whatever
})
it("runs asynchronously", doneOrContext => {
  setTimeout(() => {
    // call functions and assert whatever
    doneOrContext();
  }, 100)
})



md5-b1fe6f00c87a2916712cf6a4df16e142



it("runs immediately using the parameter as a context", doneOrContext => {
  doneOrContext.configureByMutation("some value");
  // As well as changing config, also flags to Mocha that this test is treating the
  // parameter as a context object and is therefore not async.
  // Call functions and assert whatever
})



md5-b1fe6f00c87a2916712cf6a4df16e142



it("runs asynchronously using the parameter as a context", doneOrContext => {
  doneOrContext.configureByMutation("some value");
  doneOrContext.setAsync(); // Flags to Mocha that even though the parameter has been used as
  // a context object, the test is in fact asynchronous.
  setTimeout(() => {
    // call functions and assert whatever
    doneOrContext();
    // or doneOrContext.done()
  }, 100)
})

Eu usei o script abaixo, mas recebi o mesmo erro de ultrapassagem de tempo limite.

Myscript:

describe ("getBillingDetail", função assíncrona () {
this.timeout (55000);
it.only ("verificar nome de trabalho válido fornecido", função assíncrona (concluído) {
this.timeout (55000);
var result = await url.getBillingDetail ('12254785565647858');
console.log (resultado);
assert.equal (resultado, verdadeiro);
});
});

Erro: Tempo limite de 55.000 ms excedido. Para testes assíncronos e ganchos, certifique-se de que "done ()" seja chamado; se retornar uma promessa, certifique-se de que resolve.

Não passe um retorno de chamada concluído para uma função assíncrona

Eu criei uma solução de protótipo que é compatível com versões anteriores. Por enquanto, é um módulo separado, mas a funcionalidade poderia ser facilmente mesclada em mocha propriamente dita.

https://github.com/papercuptech/mocha-lambda

Atalho

require('mocha-lambda')
// now a global '_tst' can be used where 'this' was before

describe('suite', () => {
  beforeEach(() => {
    _tst.answer = 42
  })

  it('provides "this" as "_tst"', function() {
    assert(this === _tst)
  })

  it('works', () => {
    assert(_tst.answer === 42)
    _tst.skip()
  })
})

para nomenclatura explícita (e funciona com TypeScript)

// if you were previously explicitly importing api (describe, it, etc.) from 'mocha',
// you will have to change to importing from 'mocha-lambda', until (or if) this
// gets merged into mocha proper
import ctx, {describe as d, it as i} from 'mocha-lambda'

d('suite', () => {
  // ctx() is a function that returns "this" as appropriate
  i('works using ctx()', () => {
    ctx().skip()
  })
})

import {Context} from 'mocha'
// ctx() can also rename global
ctx('t')
declare var t: Context
d('suite', () => {
  // ctx() is a function that returns "this" as appropriate
  i('works using renamed global', () => {
    t.skip()
  })
})

@papercuptech O link 404 não foi encontrado.

Woops .. era um repositório privado. agora público

Também pode npm i mocha-lambda

@aleung @ linesh-simplicity, foi substituído por # 3485

Obrigado.

Por que tantas "mágicas" que, no final, geram problemas? Por que não isso ?:

var mocha = require('mocha');

mocha.describe('foo', (suite) => {
  suite.timeout(100);

  suite.it('must love bar', () => ... );  
});

Sem globais, sem mágica problemática ... mas apenas JavaScript.

veja a resposta @ thom-nic, limpe e resolveu

Juro que li em algum lugar que você poderia fazer algo assim:

describe('a slow thing', () => {
 // test code...
}).timeout(5000);

que não altera o contrato de parâmetro para a função fornecida. Agora não consigo encontrar nenhuma referência a tal coisa, então talvez eu apenas tenha imaginado.

A solução @ thom-nic funcionou para mim, obrigado!

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

Questões relacionadas

robertherber picture robertherber  ·  3Comentários

Swivelgames picture Swivelgames  ·  3Comentários

luoxi001713 picture luoxi001713  ·  3Comentários

Aarbel picture Aarbel  ·  3Comentários

helloravi picture helloravi  ·  3Comentários