Teste Angular 2 - chamada de função assíncrona - quando usar

86

Quando você usa a função assíncrona no TestBed ao testar no Angular 2?

Quando você usa isso?

 beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [MyModule],
            schemas: [NO_ERRORS_SCHEMA],
        });
    });

E quando você usa isso?

beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [MyModule],
        schemas: [NO_ERRORS_SCHEMA],
    });
}));

Alguém pode me esclarecer sobre isso?

xiotee
fonte

Respostas:

95

asyncnão permitirá que o próximo teste seja iniciado até que asyncconclua todas as suas tarefas. O que asyncfaz é envolver o retorno de chamada em uma Zona, onde todas as tarefas assíncronas (por exemplo setTimeout) são rastreadas. Assim que todas as tarefas assíncronas forem concluídas, o é asyncconcluído.

Se você já trabalhou com Jasmine fora do Angular, pode ter visto donesendo passado para o callback

it('..', function(done) {
  someAsyncAction().then(() => {
    expect(something).toBe(something);
    done();
  });
});

Aqui, é Jasmine nativo, onde dizemos a Jasmine que esse teste deve atrasar a conclusão até que ligemos done(). Se não ligássemos done()e em vez disso fizéssemos:

it('..', function() {
  someAsyncAction().then(() => {
    expect(something).toBe(something);
  });
});

O teste seria concluído antes mesmo da expectativa, porque a promessa é resolvida depois que o teste termina de executar as tarefas síncronas.

Com o Angular (em um ambiente Jasmine), o Angular realmente fará chamadas donenos bastidores quando usarmos async. Ele manterá o controle de todas as tarefas assíncronas na Zona e, quando todas forem concluídas, doneserá chamado nos bastidores.

Em seu caso particular com a TestBedconfiguração, você a usaria geralmente quando quiser compileComponents. Eu raramente me deparo com uma situação em que eu teria que chamar de outra forma

beforeEach(async(() => {
   TestBed.configureTestingModule({
     declarations: [MyModule],
     schemas: [NO_ERRORS_SCHEMA],
   })
   .compileComponent().then(() => {
      fixture = TestBed.createComponent(TestComponent);
   });
}));

Ao testar um componente que usa templateUrl(se você não estiver usando webpack), o Angular precisa fazer uma solicitação XHR para obter o modelo, de forma que a compilação do componente seja assíncrona. Portanto, devemos esperar até que seja resolvido antes de continuar o teste.

Paul Samsotha
fonte
Ótima resposta, @peeskillet. Só para ter certeza de que entendi: quando você tem um modelo embutido, asyncnão é necessário. Quando você está usando templateUrl, é. No entanto, incluir asyncnão "quebrará" um componente de modelo embutido. Você acha que é seguro dizer que pode-se usar como padrão asyncpara todos os testes?
vince
2
@vincecampanale O templateUrl só importa durante a configuração no beforeEach. Nesse caso, você precisa ligar compileComponents. Não tem nada a ver com o uso asyncem cada teste se é isso que você está perguntando. Quanto à segurança (quando você deve ligar compileComponents), consulte Quando devo chamar compileComponents
Paul Samsotha
2
@vincecampanale Nem sempre é o caso de você querer que ele seja chamado antes do teste. Às vezes, você pode querer chamá-lo depois de fazer alguma inicialização. Você precisa entender o que a chamada realmente faz. Na maioria das vezes, deve estar OK. Mas eu pessoalmente não gosto que eles tenham tomado essa decisão por conta própria. Mas vejo muitas pessoas que se deparam com o problema em que se esquecem de chamá-lo e se perguntam por que algumas coisas não estão funcionando. Portanto, talvez seja melhor que eles gerem a chamada. O local pode ser discutível, mas pelo menos eles o chamam
Paul Samsotha
2
@vincecampanale Geralmente, quando você deseja que a visão (re) renderizada, é quando você deve chamá-la. Por exemplo, Criar componente -> renderizar visualização Mas se você deseja inicializar algo primeiro como Criar Componente -> altere o valor no componente que é usado para renderizar -> renderizar a visualização. Isso é o que quero dizer com talvez você queira inicializar algo primeiro
Paul Samsotha
1
Ah, e mais uma coisa. A primeira vez que você o chama, é quando ngOnInito componente é chamado. Às vezes, isso é importante durante o teste
Paul Samsotha
26

Quando você faz uma chamada assíncrona em seu teste, a função de teste real é concluída antes que a chamada assíncrona seja concluída. Quando você precisa verificar algum estado quando a chamada foi concluída (o que geralmente é o caso), a estrutura de teste relatará o teste como concluído enquanto ainda há trabalho assíncrono em andamento.

Com o uso, async(...)você diz à estrutura de teste para esperar até que a promessa de retorno ou observável seja concluída antes de tratar o teste como concluído.

it('should show quote after getQuote promise (async)', async(() => {
  fixture.detectChanges();

  fixture.whenStable().then(() => { // wait for async getQuote
    fixture.detectChanges();        // update view with quote
    expect(el.textContent).toBe(testQuote);
  });
}));

O código passado para then(...)será executado depois que a própria função de teste for concluída. Com async()você torna a estrutura de teste ciente de que ela precisa aguardar a conclusão de promessas e observáveis ​​antes de tratar o teste como concluído.

Veja também

Günter Zöchbauer
fonte