Como criar um teste unitário para um interceptor - Angular
Explore o passo a passo na criação de testes unitários para interceptors no Angular e descubra a importância desses testes para a qualidade e confiabilidade do seu código.
Interceptors desempenham um papel importante na manipulação de requisições HTTP em aplicações Angular. Por exemplo, é comum encontrarmos interceptors que injetam cabeçalhos de requisição ou que padronizam o tratamento de erros.
Neste artigo, exploraremos como criar testes unitários para interceptors. Os exemplos a seguir utilizam a configuração padrão do Angular, utilizando o Jasmine para a execução dos testes unitários. Se você estiver utilizando o Jest, pode ser necessário fazer pequenos ajustes, mas a lógica empregada nos exemplos será a mesma, independentemente do framework utilizado para os testes unitários.
Teste unitário - Interceptor Authorization
Como primeiro exemplo vamos construir um teste unitário para um interceptor que injeta o cabeçalho Authorization. O código fonte é o seguinte:
Analisando o código do interceptor acima, podemos observar dois possíveis caminhos (fluxos):
O this.storageService.getJWT() retornará uma string, e assim, o conteúdo dentro do bloco if será executado para injetar o valor no cabeçalho Authorization.
Se this.storageService.getJWT() retornar null ou estiver vazio, o conteúdo dentro do bloco if não será executado, e o cabeçalho de requisição Authorization não será definido.
Observação: Esses dois caminhos são identificados como "branches" durante a análise de cobertura dos testes unitários.
Agora, vamos testar esses dois cenários!
Para iniciar a construção dos testes unitários, é necessário configurar um módulo de testes. Isso pode ser feito utilizando o TestBed fornecido pelo próprio Angular. Através do método configureTestingModule, importaremos as dependências para testar uma unidade de código. Essa configuração será realizada no arquivo chamado authorization.interceptor.spec.ts.
Uma abordagem para testar um interceptor é configurá-lo no módulo de testes para "dizer" ao Angular que deve usar o interceptor em todas as requisições. Em seguida, podemos disparar requisições "mockadas", esperando que o interceptor injete ou não o cabeçalho Authorization. Além disso, será necessário simular o retorno de storageService.getJWT(). Vamos para o código!
A seguir, elaborei a estrutura do teste unitário e adicionei vários comentários para facilitar o entendimento:
Com a estrutura do teste unitário construída, estamos prontos para criar o primeiro teste. A seguir escrevi um teste em que o AuthorizationInterceptornão injeta o cabeçalhoAuthorization devido ao retorno null do método getJWT():
Para executar os testes unitários basta rodar o comando ng test:
O teste unitário foi executado com sucesso. Agora vamos analisar a cobertura de código, ou seja, quais partes do AuthorizationInterceptor foram cobertas pelo nosso teste unitário. Para realizar essa tarefa, basta executar o comando abaixo:
ng test --code-coverage --watch false
Após a execução, será gerada uma pasta chamada /coverage. Abra o arquivo index.html e navegue até o arquivo authorization.interceptor.ts:
Observe na imagem acima que nosso teste unitário não entrou no bloco if, indicando que o código que desenvolvemos para testar o AuthorizationInterceptor não contemplou um cenário em que o conteúdo dentro do bloco if fosse executado. Por essa razão, é exibido um "I" na imagem, localizado à esquerda do bloco if. Esse "I" representa "if path not taken", ou seja, nenhum teste unitário passou pelo bloco if.
Na verdade, fizemos isso de propósito, uma vez que simulamos que o método storageService.getJWT() retorna o valor null. Agora, vamos escrever um segundo teste para contemplar este cenário:
Executando novamente o comando abaixo, é possível ver nossos testes unitários cobrem 100% do código do AuthorizationInterceptor:
ng test --code-coverage --watch false
Teste unitário - Interceptor para tratamento de erros
Como segundo caso de estudo, copiei e alterei um trecho de código da documentação oficial para ilustrar a implementação de um interceptor destinado a lidar com erros durante as requisições:
Assim como no interceptor do exemplo anterior, aqui temos duas ramificações (possibilidades) no código:
Tratamento de erros quando error.status === 0: Isso ocorre em erros originados no lado do cliente devido à falta de conexão com a internet ou a alguma exceção JavaScript.
Tratamento de erros oriundos de uma resposta do backend.
Vamos abordar o teste unitário para essas situações:
Resultado:
Considerações
A criação de testes unitários para interceptors não é tão complicada quanto parece. Pessoalmente, considero isso uma espécie de "receita de bolo", pois, basicamente, todo teste unitário segue a mesma estrutura. Como sugestão, sempre que possível, desenvolva testes unitários para garantir que o seu código atenda a todos os requisitos. Isso contribuirá para a entrega de uma aplicação mais resiliente e de alta qualidade.
Link do repositório onde vou incluir mais exemplos de testes unitários além dos citados neste post: