Test unitari di AngularJS con Karma e Jasmine

⚡ Riepilogo intelligente

I test unitari di AngularJS con Karma e Jasmine verificano controller, direttive e servizi in modo isolato. Karma esegue le specifiche all'interno di browser reali, Jasmine fornisce la sintassi delle asserzioni e angular-mocks inietta le dipendenze senza un server attivo.

  • 🧱 Suddivisione degli utensili: Karma è l'esecutore dei test, Jasmine è il framework di specifica e angular-mocks fornisce strumenti di supporto per l'iniezione delle dipendenze.
  • ⚙️ Percorso di installazione: Installa karma, karma-chrome-launcher, karma-jasmine e karma-cli tramite npm, quindi genera il file karma.conf.js.
  • 📁 Regola di configurazione: L'array di file deve elencare angular.js, angular-mocks.js, i sorgenti dell'applicazione e la cartella di test, in quest'ordine.
  • Test del controller: Inietta il servizio $controller, passa un oggetto $scope fittizio e poi verifica i valori assegnati dal controller.
  • 🧩 Test delle direttive: Utilizza $compile con $rootScope, chiama $digest e ispeziona il markup renderizzato restituito dalla direttiva.
  • 🌐 Strato end-to-end: ProtracTor gestisce un'applicazione in esecuzione e conta le righe ripetute prodotte da ng-repeat.
  • ???? Stato del supporto: Il supporto per AngularJS è terminato il 31 dicembre 2021, quindi questo flusso di lavoro deve essere considerato come informazione di manutenzione pregressa.

Test unitari di AngularJS con Karma e Jasmine

Una delle caratteristiche più brillanti di AngularJS è la Collaudo aspetto. Quando gli sviluppatori a Google Nello sviluppo di AngularJS, hanno tenuto conto dei test e si sono assicurati che l'intero framework di AngularJS fosse testabile.

In AngularJS, i test vengono solitamente eseguiti utilizzando Karma (framework). È possibile eseguire i test di AngularJS anche senza Karma, ma il framework Karma offre funzionalità così eccellenti per il testing del codice AngularJS che ha senso utilizzarlo.

  • In AngularJS, possiamo eseguire Test unitari separatamente per controllori e direttive.
  • Possiamo anche eseguire test end-to-end di AngularJS, che viene testato dal punto di vista dell'utente.

⚠️ Nota sulla versione: AngularJS (versione 1.x) ha raggiunto la fine ufficiale del suo ciclo di vita il 31 dicembre 2021 e ProtracTor è stato ritirato nell'agosto 2023. Il flusso di lavoro descritto in questa pagina rimane valido per la manutenzione delle applicazioni AngularJS 1.x esistenti. I nuovi progetti dovrebbero utilizzare le versioni più recenti di Angular con un runner attualmente supportato.

Introduzione e installazione del framework Karma

Il karma è un strumento di automazione dei test creato dal team AngularJS presso GoogleIl primo passo per utilizzare Karma è installarlo. Karma si installa tramite npm (un gestore di pacchetti utilizzato per installare facilmente i moduli su una macchina locale).

Installazione del Karma

L'installazione di Karma tramite npm viene eseguita in un processo in due passaggi.

Passo 1) Esegui la riga seguente dalla riga di comando

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

In cui,

  1. npm è l'utilità da riga di comando per il gestore di pacchetti Node.js utilizzata per installare moduli personalizzati su qualsiasi macchina. Viene fornito con Node.js.
  2. Il parametro install indica all'utilità della riga di comando npm che è necessaria l'installazione.
  3. Nella riga di comando sono specificate 3 librerie necessarie per lavorare con karma.
    • karma è la libreria principale che verrà utilizzata a scopo di test.
    • karma-chrome-launcher è una libreria separata che consente al browser Chrome di riconoscere i comandi karma.
    • karma-jasmine – Questo installa jasmine che è un framework dipendente per Karma.

Passo 2) Il passaggio successivo è installare l'utilità della riga di comando karma. Ciò è necessario per eseguire i comandi della linea karma. L'utilità karma line verrà utilizzata per inizializzare l'ambiente karma per i test.

Per installare l'utilità della riga di comando, eseguire la riga seguente dalla riga di comando

npm install -g karma-cli
  • karma-cli viene utilizzato per installare l'interfaccia a riga di comando per karma, che servirà per scrivere i comandi di karma nella riga di comando. L'opzione -g lo installa a livello globale, in modo che il comando karma sia disponibile da qualsiasi cartella.

Configurazione del framework Karma

Il passo successivo è configurare il karma che può essere eseguito tramite il comando

karma init

Dopo aver eseguito il passaggio precedente, karma creerà un file karma.conf.js. Il file sarà probabilmente simile allo snippet mostrato di seguito

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

I file di configurazione sopra indicati comunicano al motore di runtime Karma le seguenti cose

  1. angolare.js – Questo indica a Karma che la tua applicazione dipende dai moduli principali di AngularJS.
  2. angular-mocks.js – Questo indica a Karma di utilizzare la funzionalità di test unitari per AngularJS dal file angular-mocks.js.
  3. Tutti i file principali dell'applicazione o della logica aziendale sono presenti nella cartella lib dell'applicazione.
  4. La cartella tests conterrà tutti gli unit test.

Suggerimento: L'ordine è importante all'interno dell'array di file. angular.js deve essere caricato prima di angular-mocks.js, ed entrambi devono essere caricati prima del codice dell'applicazione, altrimenti l'iniettore non sarà disponibile quando vengono eseguite le specifiche.

Per verificare se il karma funziona, crea un file chiamato Sample.js, inserisci il codice seguente e inseriscilo nella directory test.

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

Il codice soprastante presenta i seguenti aspetti

  1. La funzione description viene utilizzata per fornire una descrizione del test. Nel nostro caso, stiamo dando la descrizione 'Test di esempio' al nostro test.
  2. La funzione 'it' viene utilizzata per dare un nome al test. Nel nostro caso, stiamo dando il nome al nostro test come 'La condizione è vera'. Il nome del test deve essere significativo.
  3. La combinazione delle parole chiave "expect" e "toBe" indica qual è il valore atteso ed effettivo del risultato del test. Se il valore effettivo e quello atteso sono gli stessi, il test verrà superato altrimenti fallirà.

Quando esegui la seguente riga al prompt dei comandi, verrà eseguito il file di test soprastante

karma start

L'output seguente è preso dall'IDE Webstorm in cui sono state eseguite le operazioni di cui sopra.

Configurazione della Struttura del Karma

  1. L'output arriva nell'esploratore Karma interiore Webstorm. Questa finestra mostra l'esecuzione di tutti i test definiti nel quadro del karma.
  2. Qui puoi vedere che viene mostrata la descrizione del test eseguito che è “Sample test”.
  3. Successivamente, puoi vedere che il test stesso che ha il nome "La condizione è vera" viene eseguito.
  4. Tieni presente che poiché tutti i test hanno accanto l'icona verde "Ok" che simboleggia che tutti i test sono stati superati.

Cos'è Jasmine nei test unitari di AngularJS?

Karma decide dove vengono eseguiti i test, mentre Jasmine decide come vengono scritti. Jasmine è un framework di specifica basato sul comportamento (BDD) che fornisce il vocabolario utilizzato in ogni esempio di questa pagina, motivo per cui l'adattatore karma-jasmine viene installato insieme al runner.

Jasmine apporta quattro elementi fondamentali:

  • descrivere(): Raggruppa le specifiche correlate in una suite e accetta una descrizione leggibile dell'unità in fase di test.
  • Esso(): Dichiara una singola specifica. Il nome dovrebbe indicare il comportamento che viene verificato, piuttosto che il metodo che viene chiamato.
  • expect() con matcher: Confronta un valore effettivo con un valore previsto. I metodi di confronto più comuni includono toBe, toEqual, toContain e toBeDefined.
  • beforeEach() e afterEach(): Eseguire le procedure di configurazione e smontaggio prima e dopo ogni specifica, in modo da garantire l'indipendenza di ciascun test.

Jasmine offre anche gli spy. Uno spy sostituisce una funzione reale con una sua rappresentazione registrata, consentendo di testare un controller senza dover chiamare un endpoint HTTP reale. Poiché Jasmine non necessita di un DOM né di una libreria di asserzioni propria, la combinazione di Karma e Jasmine rimane il modo più leggero per eseguire AngularJS. JavaTest unitari degli script.

Test dei controller AngularJS

Il framework di test del karma ha anche la funzionalità per testare i controllori end-to-end. Ciò include il test dell'oggetto $scope utilizzato nei controller.

Diamo un'occhiata a un esempio di come possiamo raggiungere questo obiettivo.

Nel nostro esempio,

Dovremmo prima definire un controller. Questo controller eseguirà i passaggi indicati di seguito

  1. Crea una variabile ID e assegnale il valore 5.
  2. Assegna la variabile ID all'oggetto $scope.

Il nostro test verificherà l'esistenza di questo controller e verificherà anche se la variabile ID dell'oggetto $scope è impostata su 5.

Per prima cosa dobbiamo assicurarci che sia soddisfatto il seguente prerequisito

Installa la libreria angular-mocks tramite npm. Puoi farlo eseguendo la riga seguente nel prompt dei comandi.

npm install angular-mocks --save-dev

Il prossimo passo è modificare il file karma.conf.js per garantire che siano inclusi i file giusti per il test. Il segmento seguente mostra solo la parte dei file karma.conf.js che deve essere modificata

files: ['lib/angular.js', 'lib/angular-mocks.js', 'lib/index.js', 'test/*.js']
  • Il parametro 'files' indica sostanzialmente a Karma tutti i file necessari per l'esecuzione dei test.
  • I file angular.js e angular-mocks.js sono necessari per eseguire i test unitari di AngularJS.
  • Il file index.js conterrà il nostro codice per il controller
  • La cartella test conterrà tutti i nostri test AngularJS

Di seguito è riportato il nostro codice AngularJS che verrà salvato come file Index.js nella cartella test della nostra applicazione.

Il codice seguente fa semplicemente le seguenti cose

  1. Creare un Modulo AngularJS chiamato sampleApp
  2. Crea un controller chiamato AngularJSController
  3. Crea una variabile chiamata ID, assegnale il valore 5 e assegnala all'oggetto $scope
var sampleApp = angular.module('sampleApp', []);
sampleApp.controller('AngularJSController', function($scope) {
  $scope.ID = 5;
});

Una volta eseguito correttamente il codice precedente, il passaggio successivo sarà creare un file Test Case per garantire che il codice sia stato scritto ed eseguito correttamente.

Il codice per il nostro test sarà come mostrato di seguito.

Il codice sarà in un file separato chiamato ControllerTest.js, che verrà inserito nella cartella test. Il codice sottostante esegue solo le seguenti operazioni chiave

  1. Funzione beforeEach – Questa funzione viene utilizzata per caricare il nostro modulo AngularJS chiamato 'sampleApp' prima dell'esecuzione del test. Si noti che questo è il nome del modulo in un file index.js.
  2. L'oggetto $controller viene creato come oggetto mock per il controller "AngularJSController" definito nel nostro file index.js. In qualsiasi tipo di test unitario, un oggetto mock rappresenta un oggetto fittizio che verrà effettivamente utilizzato per il test. Questo oggetto mock simulerà il comportamento del nostro controller.
  3. beforeEach(inject(function(_$controller_) – Viene utilizzato per iniettare l'oggetto mock nel nostro test in modo che si comporti come il controller effettivo.
  4. var $ambito = {}; Questo è un oggetto fittizio creato per l'oggetto $scope.
  5. var controller = $controller('AngularJSController', { $scope: $scope }); – Qui verifichiamo l'esistenza di un controller chiamato 'AngularJSController'. Qui assegniamo anche tutte le variabili dall'oggetto $scope del nostro controller nel file Index.js all'oggetto $scope nel nostro file di test.
  6. Infine, confrontiamo $scope.ID con 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);
    });
  });
});

Il test precedente verrà eseguito nel browser karma e darà lo stesso risultato di superamento mostrato nell'argomento precedente.

Testare le direttive AngularJS

Il framework di test Karma ha anche la funzionalità di testare direttive personalizzate. Questo include il modelloURL's che vengono utilizzati all'interno delle direttive personalizzate.

Diamo un'occhiata a un esempio di come possiamo raggiungere questo obiettivo.

Nel nostro esempio, definiremo prima una direttiva personalizzata che esegue le seguenti operazioni

  1. Crea un modulo AngularJS chiamato sampleApp
  2. Crea una direttiva personalizzata con il nome – guru99
  3. Crea una funzione che restituisca un modello con un tag di intestazione che visualizzi il testo "This is AngularJS Testing".
var sampleApp = angular.module('sampleApp', []);
sampleApp.directive('guru99', function () {
  return {
    restrict: 'E',
    replace: true,
    template: '<h1>This is AngularJS Testing</h1>'
  };
});

Una volta eseguito correttamente il codice precedente, il passaggio successivo sarà creare un caso di test per garantire che il codice sia stato scritto ed eseguito correttamente. Il codice per il nostro test sarà come mostrato di seguito

Il codice sarà in un file separato chiamato DirectiveTest.js, che verrà inserito nella cartella test. Il codice sottostante esegue solo le seguenti operazioni chiave

  1. Funzione beforeEach – Questa funzione viene utilizzata per caricare il nostro modulo AngularJS chiamato 'sampleApp' prima dell'esecuzione del test.
  2. Il servizio $compile viene utilizzato per compilare la direttiva. Questo servizio è obbligatorio e deve essere dichiarato affinché AngularJS possa utilizzarlo per compilare la nostra direttiva personalizzata.
  3. Il `$rootScope` è lo scope primario di qualsiasi applicazione AngularJS. Abbiamo già visto l'oggetto `$scope` del controller nei capitoli precedenti. Ebbene, l'oggetto `$scope` è un oggetto figlio dell'oggetto `$rootScope`. Il motivo per cui viene dichiarato qui è che stiamo apportando una modifica a un tag HTML effettivo nel DOM tramite la nostra direttiva personalizzata. Pertanto, dobbiamo utilizzare il servizio `$rootScope` che si mette in ascolto o rileva quando si verifica una modifica all'interno di un documento HTML.
  4. var element = $compile(“ ”)($rootScope) – Questo viene utilizzato per verificare se la nostra direttiva viene iniettata come dovrebbe. La direttiva è registrata con il nome in formato camel case guru99, quindi AngularJS corrisponde all'elemento in minuscolo nel markup. Pertanto, questa istruzione viene utilizzata per effettuare tale controllo.
  5. expect(element.html()).toContain(“This is AngularJS Testing”) – Questo viene utilizzato per indicare alla funzione expect di trovare l'elemento (nel nostro caso il tag h1) che contiene il testo innerHTML “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");
  });
});

Il test precedente verrà eseguito nel browser karma e darà lo stesso risultato di superamento mostrato nell'argomento precedente.

Test end-to-end di applicazioni AngularJS

Il framework di test karma insieme a un framework chiamato ProtracTor offre la funzionalità di testare un'applicazione web end-to-end.

Quindi non si tratta solo di testare direttive e controller, ma anche di testare qualsiasi altra cosa che possa apparire su una pagina HTML.

Diamo un'occhiata a un esempio di come possiamo raggiungere questo obiettivo.

Nel nostro esempio seguente, avremo un'applicazione AngularJS che crea una tabella dati utilizzando la direttiva ng-repeat.

  1. Per prima cosa creiamo una variabile chiamata "tutorial" e le assegniamo alcune coppie chiave-valore in un unico passaggio. Ciascuna coppia chiave-valore verrà utilizzata come dati durante la visualizzazione della tabella. La variabile tutorial viene quindi assegnata all'oggetto scope in modo che sia possibile accedervi dalla nostra vista.
  2. Per ogni riga di dati nella tabella, utilizziamo la direttiva ng-repeat. Questa direttiva attraversa ciascuna coppia chiave-valore nell'oggetto ambito del tutorial utilizzando la variabile ptutor.
  3. Infine, stiamo utilizzando il file tag insieme alle coppie chiave-valore (ptutor.Name e ptutor.Description) per visualizzare i dati della tabella.
<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"}
  ];
});

Una volta eseguito correttamente il codice precedente, il passaggio successivo sarà creare un caso di test per garantire che il codice sia stato scritto ed eseguito correttamente. Il codice per il nostro test sarà come mostrato di seguito

Il nostro test testerà effettivamente la direttiva ng-repeat e garantirà che contenga 3 righe di dati come dovrebbe dall'esempio precedente.

Per prima cosa dobbiamo assicurarci che sia soddisfatto il seguente prerequisito

Installa il protracLibreria Tor tramite npm. Questo può essere fatto eseguendo la riga seguente nel prompt dei comandi.

npm install -g protractor

Il codice per il nostro test sarà come mostrato di seguito.

Il codice sarà in un file separato chiamato CompleteTest.js , che sarà inserito nella cartella test. Il codice sottostante esegue solo le seguenti operazioni chiave

  1. La funzione browser è fornita dal protracla libreria tor e presuppone che la nostra applicazione AngularJS (con il codice mostrato sopra) sia in esecuzione sul nostro sito URL – http://localhost:8080/Guru99 /
  2. var list = element.all(by.repeater('ptutor in tutorial')); -Questa riga di codice in realtà recupera la direttiva ng-repeat che viene popolata dal codice 'ptutor in tutorial'. L'elemento e by.repeater sono parole chiave speciali fornite dal protracLibreria Tor che ci permette di ottenere i dettagli della direttiva ng-repeat.
  3. aspetta(list.count()).toEqual(3); – Infine, stiamo utilizzando la funzione wait per vedere che stiamo effettivamente ottenendo 3 elementi popolati nella nostra tabella come risultato della direttiva 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);
  });
});

Il test precedente verrà eseguito nel browser karma e darà lo stesso risultato di superamento mostrato nell'argomento precedente.

Karma contro Jasmine contro Protractor: Differenze chiave

I tre strumenti utilizzati in questa pagina vengono spesso confusi perché compaiono nella stessa riga di comando. Ciascuno di essi occupa un livello diverso dello stack di test, come chiarisce la tabella sottostante.

Parametro Karma Jasmine Protractor
Categoria Corridore di prova Quadro di specificazione Framework di automazione end-to-end
Lavoro principale Avvia i browser e riporta i risultati Fornisce descrivere, esso e aspettarsi Esegue un'applicazione in tempo reale in un browser reale
Livello di prova Unità e integrazione Unità e integrazione Da un capo all'altro
Necessita di un server in esecuzione Non Non Si
File di configurazione karma.conf.js Nessuno, funziona all'interno di un runner PROtractor.conf.js
Stato attuale Obsoleto, solo manutenzione Mantenuto attivamente In pensione dall'agosto 2023

In breve, Jasmine scrive il test, Karma lo esegue e ProtracTor estende la stessa sintassi di Jasmine a una pagina completamente renderizzata.

Migliori pratiche per i test unitari di AngularJS

Un piccolo insieme di abitudini mantiene una suite AngularJS veloce e affidabile man mano che l'applicazione cresce.

  1. Testare un'unità alla volta. Carica solo il modulo da testare all'interno di beforeEach e simula ogni collaboratore in modo che un errore punti a un singolo file.
  2. Non chiamare mai un vero backend. Utilizza il servizio $httpBackend di angular-mocks per restituire risposte predefinite. I test vengono quindi eseguiti offline e producono risultati identici su ogni macchina.
  3. Mantieni i nomi delle specifiche comportamentali. Un nome come "restituisce 5 all'inizializzazione del controller" risulta più leggibile in un report rispetto a "test1".
  4. Eseguire sempre un lavaggio completo del ciclo di digestione. Chiama $rootScope.$digest() dopo aver modificato i dati dello scope, altrimenti i binding e i watcher non si aggiornano e le asserzioni falliscono per il motivo sbagliato.
  5. Misurate la copertura, ma valutatela con attenzione. Aggiungi la copertura del karma per visualizzare i rami non testati. Un'elevata copertura con asserzioni deboli nasconde comunque i difetti.
  6. Eseguire la suite in modalità di integrazione continua. Utilizza un launcher Chrome headless in modo che le stesse specifiche vengano eseguite a ogni commit, anziché solo sulla macchina dello sviluppatore.

L'applicazione di queste regole trasforma gli esempi di cui sopra da dimostrazioni isolate in una rete di sicurezza di regressione per una codebase AngularJS legacy. Per un quadro più ampio, vedere il Tutorial su AngularJS serie.

DOMANDE FREQUENTI

Il modulo che contiene quel provider non è stato caricato. Aggiungilo all'interno di beforeEach(module('yourModule')) e verifica che angular-mocks.js sia elencato in karma.conf.js dopo angular.js, altrimenti l'iniettore non può risolvere la dipendenza.

Installa karma-chrome-launcher e imposta l'opzione del browser su ChromeHeadless nel file karma.conf.js. La suite verrà quindi eseguita all'interno di pipeline di integrazione continua e container in cui non è disponibile un server di visualizzazione.

Sì. Gli assistenti di programmazione basati sull'IA leggono un controller e redigono le specifiche Jasmine con ambiti e matcher fittizi. RevEsaminate attentamente l'output, perché le asserzioni generate spesso si limitano a riformulare l'implementazione anziché verificarne il comportamento previsto.

Sì. Gli strumenti di intelligenza artificiale traducono le specifiche di Jasmine nella sintassi di Jest o Vitest e segnalano le chiamate ad angular-mocks che non hanno un equivalente diretto. La verifica manuale rimane essenziale, poiché i modelli di dependency injection raramente vengono convertiti correttamente.

La maggior parte dei team punta a una copertura del codice tra il 70 e l'80% per controller e servizi. Raggiungere una percentuale più alta di solito implica testare modelli e librerie di terze parti, il che aumenta i costi di manutenzione senza individuare difetti reali.

Riassumi questo post con: