Jest: Fornece uma API para liberar a fila de resolução de promessa

Criado em 23 nov. 2016  ·  46Comentários  ·  Fonte: facebook/jest

Você quer solicitar um recurso ou relatar um bug ?

_Feature_, eu acho, mas muito importante ao testar o código que usa Promise s.

Qual é o comportamento atual?

Eu tenho um componente que usa Promise empacotamento e encadeamento internamente no acompanhamento de uma ação assíncrona externa. Estou fornecendo a simulação da ação assíncrona e resolvendo a promessa que ela retorna no meu teste.

O componente é mais ou menos assim:

class Component extends React.Component {
  // ...
  load() {
    Promise.resolve(this.props.load())
      .then(
        result => result
          ? result
          : Promise.reject(/* ... */)
        () => Promise.reject(/* ... */)
      )
      .then(result => this.props.afterLoad(result));
  }
}

E o código de teste é mais ou menos assim:

const load = jest.fn(() => new Promise(succeed => load.succeed = succeed));
const afterLoad = jest.fn();
const result = 'mock result';
mount(<Component load={load} afterLoad={afterLoad} />);
// ... some interaction that requires the `load`
load.succeed(result);
expect(afterLoad).toHaveBeenCalledWith(result);

O teste falha porque expect() é avaliado antes dos manipuladores de promessa encadeados. Tenho que replicar o comprimento da cadeia de promessa interna no teste para conseguir o que preciso, como este:

return Promise.resolve(load.succeed(result))
  // length of the `.then()` chain needs to be at least as long as in the tested code
  .then(() => {})
  .then(() => expect(result).toHaveBeenCalledWith(result));

Qual é o comportamento esperado?

Eu esperaria que Jest fornecesse algum tipo de API para liberar todos os manipuladores de promessa pendentes, por exemplo:

load.succeed(result);
jest.flushAllPromises();
expect(result).toHaveBeenCalledWith(result);

Tentei runAllTicks e runAllTimers sem sucesso.


_Alternativamente, se estou apenas perdendo algum recurso ou padrão já existente, espero que alguém aqui me indique a direção certa:) _

Enhancement New API proposal

Comentários muito úteis

Uma função auxiliar pode transformar isso em uma promessa, então você não precisa lidar com o retorno de chamada concluído. É pequeno o suficiente, é bastante inofensivo para mantê-lo no userland, mas eu não reclamaria se fosse colocado no objeto de brincadeira. Algo assim é muito usado em meus projetos.

function flushPromises() {
  return new Promise(resolve => setImmediate(resolve));
}

test('', () => {
  somethingThatKicksOffPromiseChain();
  return flushPromises().then(() => {
    expect(...
  });
})

Com o async await é quase bonito:

test('', async () => {
  somethingThatKicksOffPromiseChain();
  await flushPromises();
  expect(...
})

Todos 46 comentários

Embora o teste assíncrono prometa, é bom lembrar que você pode retornar uma função de teste como uma promessa, então algo como isto funcionará:

test('my promise test', () => { //a test function returning a Promise
  return Promise.resolve(load.succeed(result))
    .then(() => {})
    .then(() => expect(result).toHaveBeenCalledWith(result));
})

Retornar uma promessa da função de teste torna Jest ciente de que este é um teste assíncrono e deve esperar até que seja resolvido ou expire.

@thymikee É claro que estou devolvendo o valor para fazer Jest esperar - isso é completamente .then(() => {}) em seu código. Não vejo como posso descrever o problema de forma mais concisa do que já fiz no post de abertura. Leia com atenção e reabra o problema ou descreva como contorná-lo.

_Adicionei return ao código no OP para evitar confusão._

Encontrou um problema semelhante e o descreveu aqui: https://github.com/pekala/test-problem-example

Resumindo: estou tentando afirmar a sequência de ações despachadas para a loja Redux como resultado da interação do usuário (simulada usando enzima). As ações como despachadas são sincronizadas e assíncronas usando Promises (simulado para resolver imediatamente). Parece não haver maneira de afirmar depois que a cadeia de promessas foi encerrada, se você não tiver acesso direto à cadeia de promessas. setTimeout(..., 0) funciona, mas parece hacky e se a afirmação no retorno de chamada de setTimeout falhar, Jest falha com erro de tempo limite (em vez de erro de afirmação).

A ideia de flushAllPromises parece uma solução, embora eu ache que é isso que runAllTicks deve fazer?

Como acompanhamento: Tentei substituir setTimeout(..., 0) por setImmediate e isso parece executar as afirmações depois que a fila de microtarefas de retorno de chamada de promessa se esgota e impede que Jest expire em erros de afirmação. Então, isso funciona bem e é uma solução aceitável para o meu caso de uso:

test('changing the reddit downloads posts', done => {
    setImmediate(() => {
        // assertions...
        done()
    })
})

Uma função auxiliar pode transformar isso em uma promessa, então você não precisa lidar com o retorno de chamada concluído. É pequeno o suficiente, é bastante inofensivo para mantê-lo no userland, mas eu não reclamaria se fosse colocado no objeto de brincadeira. Algo assim é muito usado em meus projetos.

function flushPromises() {
  return new Promise(resolve => setImmediate(resolve));
}

test('', () => {
  somethingThatKicksOffPromiseChain();
  return flushPromises().then(() => {
    expect(...
  });
})

Com o async await é quase bonito:

test('', async () => {
  somethingThatKicksOffPromiseChain();
  await flushPromises();
  expect(...
})

@jwbay isso é um bom açúcar aí mesmo 🍠!

É verdade que esse flushPromises acabou sendo uma linha única, mas não é nada óbvio como chegar a essa linha. Portanto, acho que seria uma vantagem para os usuários do Jest tê-lo disponível como uma função util.

@pekala o liner IMO não fornece o comportamento necessário porque não vai esperar até que a seguinte promessa pendente seja resolvida:

function foo() {  
  return new Promise((res) => {
    setTimeout(() => {
      res()
    }, 2000);
  });
}

E quanto ao swizzling Promise e, quando uma nova Promise for criada, adicione-a a algum array e, em seguida, libere todas as promessas que aguardarão em Promise.all neste array?

@talkol acho que sim, contanto que você

@pekala não há necessidade de falsificar cronômetros com este exemplo, pois a promessa será resolvida somente depois que o tempo for atingido
Eu só estou preocupado que Swizzling Promise irá bagunçar o funcionamento interno da piada, é um pouco difícil

Se você não falsificar temporizadores, seus testes levarão 2 segundos + reais para serem concluídos. Acho que a prática recomendada seria remover esses tipos de atrasos, caso em que o flushPromises proposto por @jwbay dá conta do

Tudo depende do que você está tentando testar :) Tudo o que estou dizendo é que os temporizadores são uma preocupação não relacionada à espera pelas promessas

Estamos enfrentando problemas relacionados a Promises not resolve, que são mescladas com chamadas setTimeout. No jest v19.0.2 não temos problemas, mas no jest v20.0.0 as promessas nunca entram nas funções de resolução / rejeição e, portanto, os testes falham. Nosso problema parece estar relacionado a não ter _uma API para liberar a fila de resolução de promessa_, mas esse problema parece ser anterior a jest v20.0.0, onde começamos a ver o problema, então não tenho certeza absoluta.

Esta é a única solução que conseguimos encontrar para alguns de nossos testes, uma vez que temos uma série de setTimeout s alternados e Promise s usados ​​no código que eventualmente chama o onUpdateFailed callback.

  ReactTestUtils.Simulate.submit(form);
  return Promise.resolve()
    .then(() => { jest.runOnlyPendingTimers(); })
    .then(() => { jest.runOnlyPendingTimers(); })
    .then(() => { jest.runOnlyPendingTimers(); })
    .then(() => {
      expect(onUpdateFailed).toHaveBeenCalledTimes(1);
      expect(getErrorMessage(page)).toEqual('Input is invalid.');
    });

Não é tão bonito, então qualquer conselho aqui será muito apreciado.

Outro exemplo em que você não pode retornar a promessa do teste:

describe('stream from promise', () => {
  it('should wait till promise resolves', () => {
    const stream = Observable.fromPromise(Promise.resolve('foo'));
    const results = [];
    stream.subscribe(data => { results.push(data); });
    jest.runAllTimers();
    expect(results).toEqual(['foo']);
  });
});

Este teste falha com o jest 20.0.4.

A solução de @philwhln também pode ser escrita com async / await

ReactTestUtils.Simulate.submit(form);

await jest.runOnlyPendingTimers();
await jest.runOnlyPendingTimers();
await jest.runOnlyPendingTimers();

expect(onUpdateFailed).toHaveBeenCalledTimes(1);
expect(getErrorMessage(page)).toEqual('Input is invalid.');

Eu adoraria uma função utilitária que esvaziasse a fila de promessas

Eu adoraria uma função que liberasse as filas de promessas entre os testes também.

Estou testando o código que usa Promise.all para envolver várias promessas. Quando uma dessas promessas embrulhadas falha (porque é isso que eu quero testar), a promessa retorna imediatamente, o que significa que as outras promessas às vezes (condição de corrida, não determinística) retornam enquanto o próximo teste está sendo executado.

Isso causa todos os tipos de confusão com meus testes com resultados imprevisíveis / repetíveis.

Para implementar isso adequadamente, precisaríamos simular Promise para que possamos eventualmente ver todas as micro tarefas enfileiradas para resolvê-las de forma síncrona. Algo no caminho do que a simulação de promessa está fazendo.

Já existe uma API para liberar as micro tarefas enfileiradas com process.nextTick e essa API provavelmente também deve funcionar com Promises ( jest.runAllTicks ).

Eu tinha uma solução com o jasmine que se conectava ao nextTick de Yaku, uma biblioteca de promessas, capturava as chamadas do nextTick e permitia reproduzi-las mais cedo.
No entanto, o gracejo usa promessas por si mesmo, o que tornou isso problemático.
No final, peguei Yaku e o hackeado para ter um método de flush que esvazia sua fila. Por padrão, ele é executado normalmente usando nextTick, mas se você chamar flush, todos os manipuladores de promessa pendentes são executados.
A fonte está aqui:
https://github.com/lukeapage/yaku-mock
Poderia fazer uma arrumação, entrar em contato com ysmood para ver o que eles acham disso e adicionar documentação, mas praticamente faz o que você quer e funcionou para mim como uma solução simples para fazer a sincronização de promessas em testes.

Como uma solução simples para isso, gosto da solução do

Que tal adicionarmos algo semelhante ao objeto jest ?

await jest.nextTick();

Implementado como

const nextTick = () => new Promise(res => process.nextTick(res));

cc @cpojer @SimenB @rogeliog

Estou usando uma enzima para montar os componentes do React.

Eu também tenho funções que esperam que o Promises execute, mas nenhuma das correções mencionadas funcionou. Eu seria capaz de manipulá-los de forma síncrona em meu teste - se - as funções retornassem os objetos Promise, usando await , mas infelizmente as funções não retornariam os objetos Promise.

Esta é a solução alternativa que acabei fazendo usando um espião na função Promise global.

global.Promise = require.requireActual('promise');

it('my test', async () => {
    const spy = sinon.spy(global, 'Promise');

    wrapper.props().dispatch(functionWithPromiseCalls());

    for (let i = 0; i < spy.callCount; i += 1) {
      const promise = spy.getCall(i);
      await promise.returnValue;
    }

    expect(...)
});

Eu encontrei um caso de uso para isso (obrigado @jwbay pela técnica incrível)

Por exemplo, você deseja verificar se sua função tem um tempo limite e se o tempo limite é aplicado com precisão:

      jest.useFakeTimers();
      const EXPECTED_DEFAULT_TIMEOUT_MS = 10000;

      const catchHandler = jest.fn().mockImplementationOnce(err => {
        expect(err).not.toBeNull();
        expect(err.message).toContain('timeout');
      });

      // launch the async func returning a promise
      fetchStuffWithTimeout().catch(catchHandler);

      expect(catchHandler).not.toHaveBeenCalled(); // not yet

      jest.advanceTimersByTime(EXPECTED_DEFAULT_TIMEOUT_MS - 1);
      await flushPromises();

      expect(catchHandler).not.toHaveBeenCalled(); // not yet

      jest.advanceTimersByTime(1);
      await flushPromises();

      expect(catchHandler).toHaveBeenCalledTimes(1); // ok, rejected precisely

retornar uma promessa não permite verificar o momento preciso da resolução / rejeição.

Uma promessa de liberação é necessária aqui. Sem ele, a expectativa é chamada muito cedo.

Espero que isso ajude a reduzir o problema.

Para as pessoas acompanhando, há um PR aberto para isso aqui: # 6876

Publicação cruzada de https://github.com/airbnb/enzyme/issues/1587

Eu me pergunto se o padrão a seguir deve ser suficiente para resolver esse problema, e se estou fazendo algo que é considerado uma prática ruim e não deveria estar fazendo.

O que as pessoas pensam sobre essa abordagem?

export class MyComponent extends React.Component {
  constructor (props) {
    super(props)

    this.hasFinishedAsync = new Promise((resolve, reject) => {
      this.finishedAsyncResolve = resolve
    })
  }

  componentDidMount () {
    this.doSomethingAsync()
  }

  async doSomethingAsync () {
    try {
      actuallyDoAsync()
      this.props.callback()
      this.finishedAsyncResolve('success')
    } catch (error) {
      this.props.callback()
      this.finishedAsyncResolve('error')
    }
  }

  // the rest of the component
}

E nos testes:

it(`should properly await for async code to finish`, () => {
  const mockCallback = jest.fn()
  const wrapper = shallow(<MyComponent callback={mockCallback}/>)

  expect(mockCallback.mock.calls.length).toBe(0)

  await wrapper.instance().hasFinishedAsync

  expect(mockCallback.mock.calls.length).toBe(1)
})

Tive um problema quando a chamada assíncrona não foi feita diretamente em componentDidMount, mas estava chamando uma função assíncrona, que estava chamando outra função assíncrona e assim por diante. Se eu adicionasse uma etapa assíncrona extra em toda a cadeia assíncrona, precisaria adicionar um .then() extra ou um await extra, mas isso está funcionando muito bem.

Existe uma razão pela qual eu não deveria usar essa abordagem ou isso parece bom para as pessoas?

Eu fui em uma aventura ao fazer isso no userland e descobri que é realmente viável e não tão ruim (embora existam algumas armadilhas para enfrentar se você não tiver um mapa). Aqui está um relato de experiência (espero) detalhado o suficiente para ser usado diretamente ; um TLDR é transpilar async / await em promessas e trocar as promessas nativas por bluebird e temporizadores nativos por lolex; transpilar tudo , incluindo node_modules/ ; queueMicrotask é o primitivo que você precisa para promessas, mas por padrão lolex não o fornecerá porque o JSDOM não o fornecerá.

Encontrei o mesmo problema com os componentes jest.mockAllTimers() e React que chamam Promise em componentDidMount() .

A solução de # issuecomment-279171856 resolveu o problema de maneira elegante.

Precisamos de algo semelhante na API oficial do Jest!

Recentemente, encontrei um problema ao atualizar um monte de coisas, que revelou um problema em um monte de testes em que nem sempre esperávamos que as promessas terminassem. E embora métodos como await new Promise(resolve => setImmediate(resolve)); funcionassem em casos simples, descobri em meus testes que teria que executá-lo algumas vezes para limpar o tubo. Que é o que @quasicomputational mencionou em sua exploração aqui . Infelizmente, não acho que haja uma maneira de saber quando o tubo está limpo sem interceptar as promessas à medida que são criadas. Então eu criei uma pequena biblioteca para fazer isso ... promessa de espionagem . Porém, eu tive um teste que estava usando timers falsos e não funcionou com isso ... então ainda não é uma solução totalmente funcional.

Embora eu também imagine que eles só funcionem com async / await alls em seu código a ser testado SE forem transpilados para promessas. Se eles não forem transpilados para promessas, esta biblioteca não será capaz de conectá-los e esperar que sejam concluídos.

Encontrei-me com o mesmo problema e percebi:
não devemos liberar as promessas pendentes, mas, em vez disso, devemos fazer com que todo o teste falhe se houver promessas pendentes.
Dessa forma, seremos forçados a abortar as promessas pendentes dentro do código testado usando o Controlador de Abortar:
https://developers.google.com/web/updates/2017/09/abortable-fetch
Ter promessas de brincadeira é igual a dizer "A simultaneidade é difícil, então não vamos testá-la". Na realidade, deveria ser exatamente o oposto.
Como a simultaneidade é difícil, devemos testá-la ainda mais e não permitir que um teste passe com promessas pendentes.

Dada a confusão sobre o aborto de promessas nesta questão Stackoverflow, está claro que não é (AINDA) uma coisa fácil de fazer:
https://stackoverflow.com/a/53933849/373542
Vou tentar escrever uma implementação do KISS para abortar minhas promessas de busca e postarei o resultado aqui.

@ giorgio-zamparelli: _ "Simultaneidade é difícil, então não vamos testá-la" _ está completamente fora do ponto do relatório original. O problema não está relacionado com promessas _pendentes_, mas sim com o fato de que aguardar a propagação da _resolução_ da promessa por meio de código assíncrono em testes é desnecessariamente difícil.

Acho que promessas irrelevantes seriam curar os sintomas em vez da doença.

As promessas devem resolver normalmente nos testes, sem a necessidade de serem lavadas.
Se houver uma promessa pendente em seu teste, você deve esperar que seja resolvida usando, por exemplo, wait de @testing-library/react OU se a promessa pendente não fizer parte do escopo do teste, você também deve simule o código que o inicia ou você deve abortar a promessa pendente em algum lugar como no evento de ciclo de vida React willUnmount usando o AbortController

O AbortController é uma nova API que quase ninguém está usando e tenho a sensação de que é a solução para a maioria das promessas suspensas nos testes.

PROVE-ME ERRADO:
Eu poderia facilmente ser provado que estou errado se alguém que relatou ter problemas com problemas pendentes neste problema já tentou usar AbortController e jest.mock e não foi o suficiente.

@ giorgio-zamparelli: Talvez o mal-entendido resulte do meu uso da frase _ "liberar todos os manipuladores de promessa pendentes" _ (e se isso acontecer, sinto muito). Como você provavelmente verá se leu a descrição do problema completamente, eu quis dizer "manipuladores pendentes de promessas".

Portanto, para reiterar, não estamos falando sobre promessas _pendentes_ aqui (de qualquer forma), mas sim sobre liberar a resolução da promessa com o mínimo de aborrecimento. Ou, em outras palavras, sobre ir de forma transparente e determinística desde o ponto em que uma promessa é resolvida até o ponto em que todos os efeitos subsequentes vinculados a ela são invocados (para que possamos testar o resultado disso).

Recentemente, lancei flush-microtasks para esse propósito. Ele empresta a sua implementação a partir Reagir, que é surpreendentemente mais complexo do que @jwbay 's solução aqui ou @thymikee' solução s aqui . Não tenho certeza se a complexidade faz alguma diferença significativa, mas presumo que seja responsável por casos extremos não considerados pelas outras soluções neste segmento. Só usei essa implementação porque react-testing-library usa (veja aqui ), mas não a expõe.

import { flushMicroTasks } from 'flush-microtasks'

await flushMicroTasks()

@aleclarson Existe alguma diferença entre microtarefas e promessas diretas

@ramusus Parece que flush-promises usa a mesma abordagem da solução de @jwbay .

https://github.com/kentor/flush-promises/blob/46f58770b14fb74ce1ff27da00837c7e722b9d06/index.js

RTL também copiou o código do React: https://github.com/testing-library/react-testing-library/blob/8db62fee6303d16e0d5c933ec1fab5841dd2109b/src/flush-microtasks.js

EDIT: hah, já mencionado: sorrindo:

Não tenho certeza se precisamos incorporar no Jest quando as pessoas podem usar isso? Talvez possamos criar um link para ele nos documentos? Este problema é sobre liberá-los de forma síncrona, o que eu acho que está além do que queremos fazer (especialmente porque é impossível com async-await )

A solução flushPromises funciona apenas em Promessas que são resolvidas imediatamente, mas não naquelas que ainda estão pendentes.

Hmm, bom ponto. Não sei se é possível rastrear pending promessas de alguma forma. Pode ser capaz de fazer algo inteligente com async_hooks , não tenho certeza. Provavelmente será doloroso tentar diferenciar entre as promessas criadas pelo código da área do usuário e as promessas criadas por Jest e suas dependências

Tentei encapsular / simular o objeto Promise para incluir um contador, mas não funcionou:

const _promise = window.Promise;
window.Promise = function(promiseFunction){
    // counter
    return new _promise(promiseFunction);
}

O principal problema é async funções que não usam o global Promise em tudo

Ok, eu encontrei uma maneira realmente hacky como esta .

  1. Crie um novo módulo com uma lista.
  2. Adicione suas promessas a essa lista.
  3. Resolva as promessas em seu teste e remova-as da lista.
  4. No meu caso, corro wrapper.update() da enzima. Faça aqui algo semelhante, se necessário.
  5. Repita as etapas 3 e 4 até que a lista esteja vazia.

Eu sei, não é uma boa prática ajustar o código aos testes, MAS eu uso essa lógica já na renderização do lado do servidor. Mas, no final das contas, é só esperar. ¯ \ _ (ツ) _ / ¯

Há uma atualização interessante para isso em Jest 26, onde timers falsos agora são jest.useFakeTimers('modern') ).

Eu tentei os modernos timers falsos com meus testes e, infelizmente, fez com que o hack await new Promise(resolve => setImmediate(resolve)); travasse indefinidamente. Felizmente, @sinon/fake-timers inclui vários métodos *Async() que "também interromperão o ciclo de eventos, permitindo que qualquer retorno de chamada de promessa programada execute _antes_ de executar os temporizadores.". Infelizmente, não vejo nenhuma maneira de obter o objeto clock por meio das APIs do Jest.

Alguém sabe como fazer Jest nos dar aquele objeto clock ?

Como outros, minha motivação para usar await new Promise(setImmediate); é liberar promessas resolvíveis, para que eu possa testar a unidade de seu impacto no sistema.

Parece que os cronômetros falsos "modernos" de fato têm um desempenho inferior ao de outros, pois o tempo esgotou-se aparentemente sem sentido.

Aqui estão alguns testes de unidade para descrever isso:

describe('flushing of js-queues using different timers', () => {
  beforeAll(() => {
    // It would take the failing test 5 long seconds to time out.
    jest.setTimeout(100);
  });

  it.each([
    [
      'given real timers',
      () => {
        jest.useRealTimers();
      },
    ],
    ['given no timers', () => {}],
    [
      'given "legacy" fake timers',
      () => {
        jest.useFakeTimers('legacy');
      },
    ],
    [
      // This is the the failing scenario, not working like the other timers.
      'given "modern" fake timers',
      () => {
        jest.useFakeTimers('modern');
      },
    ],
  ])(
    '%s, when using setImmediate to flush, flushes a promise without timing out',
    async (_, initializeScenarioSpecificTimers) => {
      initializeScenarioSpecificTimers();

      let promiseIsFlushed = false;

      Promise.resolve().then(() => {
        promiseIsFlushed = true;
      });

      // Flush promises
      await new Promise(setImmediate);

      expect(promiseIsFlushed).toBe(true);
    },
  );
});

Eu sinto que o teste anterior não deve falhar tanto.

Para mim, a solução alternativa era liberar as promessas usando o nó nativo "setImmediate" do pacote "timers", em vez do global "setImmediate". Tendo isso, o seguinte passa:

import { setImmediate as flushMicroTasks } from 'timers';

it('given "modern" fake timers, when using native timers to flush, flushes a promise without timing out', async () => {
  jest.useFakeTimers('modern');

  let promiseIsFlushed = false;

  Promise.resolve().then(() => {
    promiseIsFlushed = true;
  });

  // Flush micro and macro -tasks
  await new Promise(flushMicroTasks);

  expect(promiseIsFlushed).toBe(true);
});

Obrigado @aleclarson.

Esta é nossa solução para esse problema:

https://github.com/team-igniter-from-houston-inc/async-fn
https://medium.com/houston-io/how-to-unit-test-asynchronous-code-for-javascript-in-2020-41c124be2552

O código de teste pode ser escrito como:

// Note: asyncFn(), extends jest.fn() with a way to control resolving/rejecting of a promise
const load = asyncFn();

const afterLoad = jest.fn();
const result = 'mock result';

mount(<Component load={load} afterLoad={afterLoad} />);

// ... some interaction that requires the `load`

// Note: New way to controlling when promise resolves
await load.resolve(result);

expect(afterLoad).toHaveBeenCalledWith(result);

Observe como você não precisa saber nada sobre promessas de descarga ou temporizadores em execução.

@jansav nice / + 1. Fwiw, eu vi essa abordagem chamada de padrão diferido. Eu acho que isso torna os testes mais agradáveis.

Parece-me que o problema com temporizadores falsos é que eles quebram o ciclo de execução natural de como os temporizadores deveriam funcionar. Eu me pergunto por que não podemos simplesmente fazer com que as funções de execução do temporizador de brincadeira sejam assíncronas? Alterar os temporizadores para resolver de forma síncrona faz com que o código de teste pareça limpo, mas está causando esse efeito colateral massivo.

meu caso de uso:

public static resolvingPromise<T>(result: T, delay: number = 5): Promise<T> {
    return new Promise((resolve) => {
        setTimeout(
            () => {
                resolve(result);
            },
            delay
        );
    });
}

arquivo de teste:

it("accepts delay as second parameter", async () => {
    const spy = jest.fn();
    MockMiddleware.resolvingPromise({ mock: true }, 50).then(spy);
    jest.advanceTimersByTime(49);
    expect(spy).not.toHaveBeenCalled();
    jest.advanceTimersByTime(1);
    await Promise.resolve(); // without this line, this test won't pass
    expect(spy).toHaveBeenCalled();
});
Esta página foi útil?
0 / 5 - 0 avaliações