Testes unitários em AngularJS com Karma e Jasmine

⚡ Resumo Inteligente

Os testes unitários do AngularJS com Karma e Jasmine verificam controladores, diretivas e serviços de forma isolada. O Karma executa as especificações em navegadores reais, o Jasmine fornece a sintaxe de asserção e o angular-mocks injeta as dependências sem a necessidade de um servidor ativo.

  • 🧱 Divisão de ferramentas: O Karma é o executor de testes, o Jasmine é o framework de especificação e o angular-mocks fornece as funções auxiliares para injeção de dependência.
  • ⚙️ Caminho de instalação: Instale o karma, o karma-chrome-launcher, o karma-jasmine e o karma-cli através do npm e, em seguida, gere o arquivo karma.conf.js.
  • 📁 Regra de configuração: O array de arquivos deve listar angular.js, angular-mocks.js, os arquivos-fonte da aplicação e a pasta de testes, nessa ordem.
  • 🎛️ Teste do controlador: Injete o serviço `$controller`, passe um objeto `$scope` simulado e, em seguida, verifique os valores atribuídos pelo controlador.
  • 🧩 Teste de diretivas: Use `$compile` com `$rootScope`, chame `$digest` e inspecione a marcação renderizada retornada pela diretiva.
  • 🌐 Camada de ponta a ponta: ProtracO tor executa um aplicativo em andamento e conta as linhas repetidas produzidas pelo ng-repeat.
  • 📌 Status do suporte: O AngularJS chegou ao fim de sua vida útil em 31 de dezembro de 2021, portanto, considere este fluxo de trabalho como conhecimento de manutenção legado.

Testes unitários em AngularJS com Karma e Jasmine

Uma das características mais brilhantes do AngularJS é a Testes aspecto. Quando os desenvolvedores em Google Ao desenvolver o AngularJS, eles mantiveram os testes em mente e garantiram que toda a estrutura do AngularJS fosse testável.

Em AngularJS, os testes normalmente são realizados usando o Karma (framework). É possível realizar testes em AngularJS sem o Karma, mas o framework Karma possui funcionalidades tão excelentes para testar código AngularJS que faz sentido utilizá-lo.

  • No AngularJS, podemos realizar Teste de Unidade separadamente para controladores e diretivas.
  • Também podemos realizar testes de ponta a ponta do AngularJS, que está sendo testado a partir da perspectiva do usuário.

⚠️ Nota sobre a versão: O AngularJS (versão 1.x) atingiu oficialmente o fim de sua vida útil em 31 de dezembro de 2021, e o ProtracO Tor foi descontinuado em agosto de 2023. O fluxo de trabalho descrito nesta página continua válido para a manutenção de aplicações AngularJS 1.x existentes. Novos projetos devem utilizar versões modernas do Angular com um executor atualmente suportado.

Introdução e instalação da estrutura Karma

carma é um ferramenta de automação de testes Criado pela equipe do AngularJS em GoogleO primeiro passo para usar o Karma é instalá-lo. O Karma é instalado via npm (um gerenciador de pacotes usado para facilitar a instalação de módulos em uma máquina local).

Instalação do Karma

A instalação do Karma via npm é feita em duas etapas.

Passo 1) Execute a linha abaixo na linha de comando

npm install karma karma-chrome-launcher karma-jasmine --save-dev

Em que,

  1. O npm é o utilitário de linha de comando do gerenciador de pacotes do Node.js, usado para instalar módulos personalizados em qualquer máquina. Ele vem com o sistema operacional. Node.js.
  2. O parâmetro install instrui o utilitário de linha de comando npm que a instalação é necessária.
  3. Existem 3 bibliotecas especificadas na linha de comando que são necessárias para trabalhar com karma.
    • karma é a biblioteca principal que será usada para fins de teste.
    • karma-chrome-launcher é uma biblioteca separada que permite que comandos karma sejam reconhecidos pelo navegador Chrome.
    • karma-jasmine – Instala o jasmim, que é uma estrutura dependente do Karma.

Passo 2) A próxima etapa é instalar o utilitário de linha de comando karma. Isso é necessário para executar comandos da linha karma. O utilitário karma line será usado para inicializar o ambiente karma para teste.

Para instalar o utilitário de linha de comando, execute a linha abaixo na linha de comando

npm install -g karma-cli
  • O karma-cli é usado para instalar a interface de linha de comando do Karma, que será usada para escrever os comandos do Karma na linha de comando. A opção -g instala-o globalmente, de forma que o comando karma esteja disponível em qualquer pasta.

Configuração da estrutura Karma

O próximo passo é configurar o karma que pode ser feito através do comando

karma init

Após a execução da etapa acima, o karma criará um arquivo karma.conf.js. O arquivo provavelmente será parecido com o trecho mostrado abaixo

files: [
  'node_modules/angular/angular.js',
  'node_modules/angular-mocks/angular-mocks.js',
  'lib/app.js',
  'tests/*.js'
]

Os arquivos de configuração acima informam ao mecanismo de tempo de execução do karma o seguinte

  1. angular.js – Isso informa ao Karma que seu aplicativo depende dos módulos principais do AngularJS.
  2. angular-mocks.js – Isso instrui o Karma a usar a funcionalidade de Teste Unitário para AngularJS do arquivo angular-mocks.js.
  3. Todos os arquivos principais do aplicativo ou de lógica de negócios estão presentes na pasta lib do seu aplicativo.
  4. A pasta de testes conterá todos os testes de unidade.

💡 Dica: A ordem dos arquivos dentro do array é importante. O arquivo angular.js deve ser carregado antes do angular-mocks.js, e ambos devem ser carregados antes do código da sua aplicação; caso contrário, o injetor não estará disponível quando as especificações forem executadas.

Para verificar se o karma está funcionando, crie um arquivo chamado Sample.js, coloque o código abaixo e coloque-o no diretório de teste.

describe('Sample test', function() {
  it('Condition is true', function() {
    expect('AngularJS').toBe('AngularJS');
  });
});

O código acima tem os seguintes aspectos

  1. A função de descrição é usada para fornecer uma descrição do teste. No nosso caso, estamos dando a descrição 'Teste de amostra' ao nosso teste.
  2. A função 'it' é usada para dar um nome ao teste. No nosso caso, damos o nome do nosso teste como 'Condição é verdadeira'. O nome do teste precisa ser significativo.
  3. A combinação das palavras-chave 'expect' e 'toBe' indica qual é o valor esperado e real do resultado do teste. Se o valor real e esperado forem iguais, o teste será aprovado, caso contrário, falhará.

Quando você executa a seguinte linha no prompt de comando, ele executa o arquivo de teste acima

karma start

A saída abaixo foi retirada do IDE Webstorm em que foram realizadas as etapas acima.

Configuração da Estrutura Karma

  1. A saída vem no explorador Karma dentro Webstorm. Esta janela mostra a execução de todos os testes definidos na estrutura karma.
  2. Aqui você pode ver que é mostrada a descrição do teste executado que é “Teste de amostra”.
  3. A seguir, você pode ver que o próprio teste que tem o nome “Condição é verdadeira” é executado.
  4. Observe que todos os testes têm o ícone verde “Ok” próximo a eles, o que simboliza que todos os testes foram aprovados.

O que é Jasmine em testes unitários com AngularJS?

O Karma decide onde os testes são executados, enquanto o Jasmine decide como eles são escritos. O Jasmine é um framework de especificação orientado a comportamento que fornece o vocabulário usado em todos os exemplos desta página, e é por isso que o adaptador karma-jasmine é instalado junto com o executor de testes.

Jasmine contribui com quatro elementos fundamentais:

  • descrever(): Agrupa especificações relacionadas em um conjunto e aceita uma descrição legível da unidade em teste.
  • isto(): Declara uma única especificação. O nome deve indicar o comportamento que está sendo verificado, e não o método que está sendo chamado.
  • expect() com comparadores: Compara um valor real com um valor esperado. Os métodos de comparação mais comuns incluem toBe, toEqual, toContain e toBeDefined.
  • beforeEach() e afterEach(): Execute a configuração e a desmontagem antes e depois de cada especificação, o que mantém cada teste independente.

Jasmine também oferece recursos de espionagem (spys). Um spy substitui uma função real por uma função gravada, permitindo testar um controlador sem acessar um endpoint HTTP ativo. Como Jasmine não precisa de DOM nem de biblioteca de asserções própria, a combinação de Karma e Jasmine continua sendo a maneira mais leve de executar AngularJS. JavaTestes unitários de script.

Testando controladores AngularJS

A estrutura de teste de karma também possui a funcionalidade de testar Controladores de ponta a ponta. Isso inclui o teste do objeto $scope que é usado nos Controladores.

Vejamos um exemplo de como podemos conseguir isso.

Em nosso exemplo,

Primeiro precisaríamos definir um controlador. Este controlador executaria as etapas mencionadas abaixo

  1. Crie uma variável de ID e atribua o valor 5 a ela.
  2. Atribua a variável ID ao objeto $scope.

Nosso teste testará a existência deste controlador e também testará se a variável ID do objeto $scope está definida como 5.

Primeiro, precisamos garantir que o seguinte pré-requisito esteja em vigor

Instale a biblioteca angular-mocks via npm. Isso pode ser feito executando a linha abaixo no prompt de comando.

npm install angular-mocks --save-dev

O próximo passo é modificar o arquivo karma.conf.js para garantir que os arquivos corretos sejam incluídos para o teste. O segmento abaixo mostra apenas os arquivos que fazem parte do karma.conf.js que precisam ser modificados

files: ['lib/angular.js', 'lib/angular-mocks.js', 'lib/index.js', 'test/*.js']
  • O parâmetro 'files' basicamente informa ao Karma todos os arquivos necessários para a execução dos testes.
  • Os arquivos angular.js e angular-mocks.js são necessários para executar testes unitários do AngularJS.
  • O arquivo index.js conterá nosso código para o controlador
  • A pasta de teste conterá todos os nossos testes AngularJS

Abaixo está o nosso código AngularJS, que será armazenado como um arquivo Index.js na pasta de testes da nossa aplicação.

O código abaixo apenas faz o seguinte

  1. Crie uma Módulo AngularJS chamado sampleApp
  2. Crie um controlador chamado AngularJSController
  3. Crie uma variável chamada ID, atribua a ela um valor 5 e atribua-a ao objeto $scope
var sampleApp = angular.module('sampleApp', []);
sampleApp.controller('AngularJSController', function($scope) {
  $scope.ID = 5;
});

Depois que o código acima for executado com sucesso, a próxima etapa seria criar um Caso de teste para garantir que o código foi escrito e executado corretamente.

O código para nosso teste será conforme mostrado abaixo.

O código estará em um arquivo separado chamado ControllerTest.js, que será colocado na pasta de teste. O código abaixo apenas faz as seguintes coisas principais

  1. Função beforeEach – Esta função é usada para carregar nosso módulo AngularJS chamado 'sampleApp' antes da execução do teste. Observe que este é o nome do módulo em um arquivo index.js.
  2. O objeto `$controller` é criado como um objeto de simulação para o controlador "AngularJSController", que está definido no nosso arquivo `index.js`. Em qualquer tipo de teste unitário, um objeto de simulação representa um objeto fictício que será usado para o teste. Esse objeto de simulação irá reproduzir o comportamento do nosso controlador.
  3. beforeEach(inject(function(_$controller_) – Isso é usado para injetar o objeto simulado em nosso teste para que ele se comporte como o controlador real.
  4. var $escopo = {}; Este é um objeto simulado sendo criado para o objeto $scope.
  5. var controller = $controller('AngularJSController', { $scope: $scope }); – Aqui, estamos verificando a existência de um controlador chamado 'AngularJSController'. Aqui, também estamos atribuindo todas as variáveis ​​do nosso objeto $scope no controlador do arquivo Index.js ao objeto $scope no arquivo de teste.
  6. Finalmente, estamos comparando $scope.ID com 5
describe('AngularJSController', function() {
  beforeEach(module('sampleApp'));

  var $controller;

  beforeEach(inject(function(_$controller_){
    $controller = _$controller_;
  }));

  describe('$scope.ID', function() {
    it('Check the scope object', function() {
      var $scope = {};
      var controller = $controller('AngularJSController', { $scope: $scope });
      expect($scope.ID).toEqual(5);
    });
  });
});

O teste acima será executado no navegador karma e fornecerá o mesmo resultado de aprovação mostrado no tópico anterior.

Testando diretivas AngularJS

A estrutura de testes Karma também possui a funcionalidade de testar diretivas personalizadas. Isso inclui o modelo.URL's que são usados ​​em diretivas personalizadas.

Vejamos um exemplo de como podemos conseguir isso.

Em nosso exemplo, primeiro definiremos uma diretiva customizada que faz o seguinte

  1. Crie um módulo AngularJS chamado sampleApp
  2. Crie uma diretiva personalizada com o nome – guru99
  3. Crie uma função que retorne um modelo com uma tag de cabeçalho que exibe o texto “Este é o teste AngularJS”.
var sampleApp = angular.module('sampleApp', []);
sampleApp.directive('guru99', function () {
  return {
    restrict: 'E',
    replace: true,
    template: '<h1>This is AngularJS Testing</h1>'
  };
});

Depois que o código acima for executado com sucesso, a próxima etapa seria criar um caso de teste para garantir que o código foi escrito e executado corretamente. O código do nosso teste será conforme mostrado abaixo

O código estará em um arquivo separado chamado DirectivaTest.js, que será colocado na pasta de teste. O código abaixo apenas faz as seguintes coisas principais

  1. Função beforeEach – Esta função é usada para carregar nosso módulo AngularJS chamado 'sampleApp' antes da execução do teste.
  2. O serviço `$compile` é usado para compilar a diretiva. Este serviço é obrigatório e precisa ser declarado para que o AngularJS possa usá-lo para compilar nossa diretiva personalizada.
  3. O `$rootScope` é o escopo principal de qualquer aplicação AngularJS. Já vimos o objeto `$scope` do controlador em capítulos anteriores. Bem, o objeto `$scope` é um objeto filho do objeto `$rootScope`. A razão pela qual ele é declarado aqui é porque estamos fazendo uma alteração em uma tag HTML real no DOM por meio de nossa diretiva personalizada. Portanto, precisamos usar o serviço `$rootScope`, que de fato escuta ou sabe quando ocorre qualquer alteração dentro de um documento HTML.
  4. var element = $compile(“ ”)($rootScope) – Isso é usado para verificar se nossa diretiva está sendo injetada corretamente. A diretiva está registrada com o nome em camel case guru99, então o AngularJS reconhece o elemento em minúsculas. na marcação. Portanto, esta declaração é usada para fazer essa verificação.
  5. expect(element.html()).toContain(“This is AngularJS Testing”) – Isso é usado para instruir a função expect a encontrar o elemento (em nosso caso, a tag h1) que contém o texto innerHTML de “This is AngularJS Testing”.
describe('Unit testing directives', function() {
  var $compile,
      $rootScope;
  beforeEach(module('sampleApp'));

  beforeEach(inject(function(_$compile_, _$rootScope_){
    $compile = _$compile_;
    $rootScope = _$rootScope_;
  }));

  it('Check the directive', function() {
    // Compile a piece of HTML containing the directive
    var element = $compile("<guru99></guru99>")($rootScope);
    $rootScope.$digest();
    expect(element.html()).toContain("This is AngularJS Testing");
  });
});

O teste acima será executado no navegador karma e fornecerá o mesmo resultado de aprovação mostrado no tópico anterior.

Testes de ponta a ponta em aplicações AngularJS

O framework de testes Karma, juntamente com um framework chamado Pro, também está incluído.tracO Tor possui a funcionalidade de testar uma aplicação web de ponta a ponta.

Portanto, não se trata apenas de testar diretivas e controladores, mas também de testar qualquer outra coisa que possa aparecer em uma página HTML.

Vejamos um exemplo de como podemos conseguir isso.

Em nosso exemplo abaixo, teremos um aplicativo AngularJS que cria uma tabela de dados usando a diretiva ng-repeat.

  1. Primeiro, estamos criando uma variável chamada “tutorial” e atribuindo a ela alguns pares de valores-chave em uma única etapa. Cada par de valores-chave será usado como dados ao exibir a tabela. A variável tutorial é então atribuída ao objeto de escopo para que possa ser acessada a partir de nossa visualização.
  2. Para cada linha de dados da tabela, estamos usando a diretiva ng-repeat. Esta diretiva passa por cada par de valores-chave no objeto de escopo do tutorial usando a variável ptutor.
  3. Finalmente, estamos usando o tag junto com os pares de valores-chave (ptutor.Name e ptutor.Descriptíon) para exibir os dados da tabela.
<table>
  <tr ng-repeat="ptutor in tutorial">
    <td>{{ ptutor.Name }}</td>
    <td>{{ ptutor.Description }}</td>
  </tr>
</table>
</div>
<script type="text/javascript">
var app = angular.module('DemoApp', []);
app.controller('DemoController', function($scope) {
  $scope.tutorial = [
    {Name: "Controllers", Description: "Controllers in action"},
    {Name: "Models", Description: "Models and binding data"},
    {Name: "Directives", Description: "Flexibility of Directives"}
  ];
});

Depois que o código acima for executado com sucesso, a próxima etapa seria criar um caso de teste para garantir que o código foi escrito e executado corretamente. O código do nosso teste será conforme mostrado abaixo

Nosso teste irá, na verdade, testar a diretiva ng-repeat e garantir que ela contenha 3 linhas de dados como deveria no exemplo acima.

Primeiro, precisamos garantir que o seguinte pré-requisito esteja em vigor

Instale o profissionaltracBiblioteca Tor via npm. Isso pode ser feito executando a linha abaixo no prompt de comando.

npm install -g protractor

O código para nosso teste será conforme mostrado abaixo.

O código estará em um arquivo separado chamado CompleteTest.js , que será colocado na pasta test. O código abaixo faz apenas as seguintes coisas principais

  1. A função de navegador é fornecida pelo protraca biblioteca Tor e pressupõe que nossa aplicação AngularJS (com o código mostrado acima) esteja em execução em nosso site. URL – http://localhost:8080/Guru99 /
  2. var list = element.all(by.repeater('ptutor in tutorial')); - Esta linha de código está, na verdade, buscando a diretiva ng-repeat, que é preenchida pelo código 'ptutor in tutorial'. O elemento e o by.repeater são palavras-chave especiais fornecidas pelo programa.tracBiblioteca Tor que nos permite obter detalhes da diretiva ng-repeat.
  3. esperar(lista.contagem()).toEqual(3); – Por último, estamos usando a função expect para ver se estamos realmente obtendo 3 itens sendo preenchidos em nossa tabela como resultado da diretiva ng-repeat.
describe('Unit testing end to end', function() {
  beforeEach(function() {
    browser.get('http://localhost:8080/Guru99/');
  });

  it('Check the ng directive', function() {
    var list = element.all(by.repeater('ptutor in tutorial'));
    expect(list.count()).toEqual(3);
  });
});

O teste acima será executado no navegador karma e fornecerá o mesmo resultado de aprovação mostrado no tópico anterior.

Karma vs Jasmine vs Profissionaltractor: Principais diferenças

As três ferramentas utilizadas nesta página são frequentemente confundidas por aparecerem na mesma linha de comando. Cada uma ocupa uma camada diferente da pilha de testes, como a tabela abaixo deixa claro.

Parâmetro Carma Jasmim Protractor
Categoria Corredor de teste Estrutura de especificação Estrutura de automação de ponta a ponta
Trabalho principal Inicia navegadores e relata resultados. Fornece descrição, isso e espera Executa uma aplicação em tempo real num navegador real.
Nível de teste Unidade e integração Unidade e integração De ponta a ponta
Precisa de um servidor em execução. Não Não Sim
Arquivo de configuração karma.conf.js Nenhuma, ela roda dentro de um corredor. prótractor.conf.js
Estado actual Obsoleto, somente para manutenção. Mantido ativamente Aposentado em agosto de 2023

Resumindo, Jasmine escreve o teste, Karma o executa e Pro...tracO Tor estende a mesma sintaxe do Jasmine para uma página totalmente renderizada.

Melhores práticas para testes unitários em AngularJS

Um pequeno conjunto de hábitos mantém um conjunto de ferramentas AngularJS rápido e confiável à medida que a aplicação cresce.

  1. Teste uma unidade de cada vez. Carregue apenas o módulo em teste dentro do `beforeEach` e simule cada colaborador para que uma falha aponte para um único arquivo.
  2. Nunca acesse um servidor backend real. Utilize o serviço `$httpBackend` do angular-mocks para retornar respostas predefinidas. Os testes são então executados offline e produzem resultados idênticos em todas as máquinas.
  3. Mantenha os nomes das especificações de acordo com o comportamento. Um nome como "retorna 5 quando o controlador é inicializado" fica melhor em um relatório do que "teste1".
  4. Limpe sempre o ciclo de digestão. Chame `$rootScope.$digest()` após alterar os dados do escopo; caso contrário, as vinculações e os observadores nunca serão atualizados e as asserções falharão pelo motivo errado.
  5. Meça a cobertura, mas avalie-a com cuidado. Adicione `karma-coverage` para visualizar branches não testadas. Alta cobertura com asserções fracas ainda oculta defeitos.
  6. Execute o conjunto de testes em integração contínua. Use um iniciador Chrome sem interface gráfica para que as mesmas especificações sejam executadas em cada commit, em vez de apenas na máquina do desenvolvedor.

Aplicar essas regras transforma os exemplos acima, de demonstrações isoladas, em uma rede de segurança contra regressões para uma base de código AngularJS legada. Para uma visão mais ampla, consulte o Tutorial de AngularJS série.

Perguntas Frequentes

O módulo que contém esse provedor não foi carregado. Adicione-o dentro de `beforeEach(module('yourModule'))` e confirme se `angular-mocks.js` está listado em `karma.conf.js` após `angular.js`, caso contrário o injetor não conseguirá resolver a dependência.

Instale o karma-chrome-launcher e defina a opção browsers para ChromeHeadless no arquivo karma.conf.js. O conjunto de ferramentas será executado em pipelines e contêineres de integração contínua onde não houver um servidor de exibição disponível.

Sim. Os assistentes de codificação de IA leem um controlador e elaboram especificações Jasmine com escopos e correspondências simuladas. RevAnalise cuidadosamente o resultado, pois as asserções geradas frequentemente reiteram a implementação em vez de verificar o comportamento pretendido.

Sim. As ferramentas de IA traduzem as especificações do Jasmine para a sintaxe do Jest ou do Vitest e sinalizam as chamadas do angular-mocks que não têm equivalente direto. A verificação manual continua sendo essencial, já que os padrões de injeção de dependência raramente se convertem sem problemas.

A maioria das equipes busca uma cobertura de instruções de 70% a 80% em controladores e serviços. Buscar um número maior geralmente significa testar modelos e bibliotecas de terceiros, o que aumenta o custo de manutenção sem detectar defeitos reais.

Resuma esta postagem com: