Relatório de cobertura de testes unitários com Angular e JEST

O objetivo deste artigo é mostrar como podemos gerar um relatório de cobertura de testes em um projeto Angular

Relatório de cobertura de testes unitários com Angular e JEST

Criando o projeto

O primeiro passo é criar o projeto e executá-lo para se certificar de que tudo está bem configurado:

ng new relatorio-cobertura
cd relatorio-cobertura/
ng serve

Se tudo correu bem você deverá ver algo como a imagem abaixo quando acessar o seguinte endereço http://localhost:4200 no seu navegador:

Aplicação angular recém criada rodando na porta 4200
Aplicação angular recém criada rodando na porta 4200

Removendo as dependências

Por padrão os projetos em Angular vêm com o Karma instalado. Como não vamos utilizá-lo, podemos removê-lo através do comando abaixo:

npm remove karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter

Os arquivos abaixo também podem ser removidos:

  • /karma.conf.js
  • /src/test.ts

Instalando e configurando JEST

Para instalar as dependências utilize o comando abaixo:

npm i -D jest @types/jest @angular-builders/jest

Abra o arquivo /tsconfig.spec.json e efetue as seguintes alterações:

  • Substitua o texto jasmine por jest
  • Remova a referência ao test.ts

O arquivo ficará da seguinte forma:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": [
      "jest",
      "node"
    ]
  },
  "files": [
    "src/polyfills.ts"
  ],
  "include": [
    "src/**/*.spec.ts",
    "src/**/*.d.ts"
  ]
}

No arquivo /tsconfig.json adicione o valor jest na propriedade "types" conforme o exemplo abaixo:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "module": "esnext",
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "lib": [
      "es2018",
      "dom"
    ],
    "types": [
      "jest"
    ]
  },
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  },
}

Por fim abra o arquivo /angular.json e troque a linha "builder": "@angular-devkit/build-angular:karma" por "builder": "@angular-builders/jest:run"

Execute o comando ng test e o resultado será:

Resultado da execução do comando ng test

Observação: quando criamos o projeto na primeira etapa, automaticamente o Angular cria um teste unitário src/app/app.component.spec.ts.

Relatório de cobertura

Para habilitar o relatório de cobertura, abra o arquivo /angular.json e adicione a propriedade "coverage": true conforme a seguinte imagem:

Adicionando a opção para gerar o relatório de cobertura dos testes unitários

Execute novamente o comando ng test. Após executá-lo, será criada uma pasta chamada /coveragena raíz do seu projeto. Abrindo o arquivo /coverage/lcov-report/index.html é possível visualizar a cobertura dos testes:

Relatório com a cobertura dos testes unitários

Para testarmos a cobertura, vamos criar uma nova função, meuTeste(), dentro do arquivo /app.component.ts:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'relatorio-cobertura';

  meuTeste() {
    this.title = 'consolelog.com.br';
  }
}

Ao executar novamente o teste, ng test, veja que o relatório aponta uma porcentagem menor (85.71%):

Relatório de cobertura - 85,71% dos statements foram cobertos

Clicando no link app.component.ts é possível visualizar quais trechos do código do componente que não foram cobertos pelos testes unitários:

O trecho destacado em vermelho indica o código que não foi testado

Aumentando a cobertura

Para cobrir este trecho de código (método meuTeste()) será necessário editar o arquivo /app.component.spec.ts e adicionar um novo teste:


  /* ... trecho acima ocultado ... */
  
  it('deve trocar o title quando meuTeste() for chamado', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();

    fixture.componentInstance.meuTeste();
    const app = fixture.componentInstance;
    expect(app.title).toEqual('consolelog.com.br');
  });

Veja que ao chamar o método meuTeste(), espera-se que o valor to title seja alterado para 'consolelog.com.br'. É justamente esta validação que nosso teste unitário efetua.

Ao executar novamente o comando ng test podemos ver que o novo trecho de código foi 100% coberto pelo novo teste unitário:

Considerações

A construção dos testes unitários é bem trabalhosa. Exige conhecimento, prática e tempo. Uma das grandes vantagens da construção dos testes unitários é que quem os constrói, precisa ter plena consciência da lógica empregada. Esta consciência permite uma eventual refatoração e/ou otimização de alguns trechos de código.