Unit Testing Angular : Jasmine & Karma (Vite)

🏷️ Front-end 📅 07/04/2026 21:00:00 👤 Mezgani Said
Testing Jasmine Karma Angular
Unit Testing Angular : Jasmine & Karma (Vite)

Setup testing Angular avec Vite, Jasmine et Karma : mocking services, testing HTTP, bonnes pratiques et couverture de code.

Introduction

Les tests unitaires sont essentiels pour une application fiable et maintenable. Angular fournit une excellente pile de testing avec Jasmine (framework de test) et Karma (test runner). Cette combinaison permet de tester les composants, services et directives en isolement.

Pourquoi tester ? Les tests attrapent les bugs avant qu'ils ne tombent en production, facilitent les refactors en confiance, et servent de documentation vivante du code.

Configuration initiale

Angular CLI configure automatiquement Jasmine + Karma lors de la création du projet :

// Générer un composant avec son fichier de test
ng generate component my-component

// Lancer les tests en watch mode
ng test

// Lancer les tests une seule fois (CI/CD)
ng test --no-watch --code-coverage
Note : Angular 17+ accepte aussi Vitest comme alternative à Karma pour une exécution plus rapide. À partir d'Angular 17, vous pouvez remplacer Karma par Vitest dans le schéma.

Écrire votre premier test

Chaque test démarre par describe() qui groupe les tests d'une classe. Puis beforeEach() initialise TestBed (le contexte d'injection d'Angular) :

// my-component.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyComponent } from './my-component.component';

describe('MyComponent', () => {
    let component: MyComponent;
    let fixture: ComponentFixture<MyComponent>;

    beforeEach(async () => {
        // Configurer le module de test avec les dépendances
        await TestBed.configureTestingModule({
            declarations: [MyComponent]
        }).compileComponents();

        // Créer une instance du composant
        fixture = TestBed.createComponent(MyComponent);
        component = fixture.componentInstance;

        // Déclencher la détection de changements (ngOnInit, etc.)
        fixture.detectChanges();
    });

    it('should create the component', () => {
        expect(component).toBeTruthy();
    });

    it('should display title in template', () => {
        const titleElement = fixture.nativeElement.querySelector('h1');
        expect(titleElement.textContent).toBe('Mon Titre');
    });

    it('should update counter when button clicked', () => {
        const button = fixture.nativeElement.querySelector('button');
        expect(component.counter).toBe(0);

        button.click();
        fixture.detectChanges();

        expect(component.counter).toBe(1);
    });
});
Points clés :
  • TestBed.configureTestingModule() crée un module isolé pour le test
  • fixture.detectChanges() force Angular à exécuter la détection de changements
  • expect().toBe() est une assertion Jasmine
  • Testez le DOM via fixture.nativeElement ou fixture.debugElement

Mocking des services

Isolez vos composants en mockant les services injectés. Ne testez jamais l'API réelle dans les tests unitaires :

// Créer un mock service
const mockUserService = {
    getUsers: jasmine.createSpy('getUsers')
        .and.returnValue(of([
            { id: 1, name: 'Alice' },
            { id: 2, name: 'Bob' }
        ]))
};

// Fournir le mock au TestBed
beforeEach(async () => {
    await TestBed.configureTestingModule({
        declarations: [UserListComponent],
        providers: [
            // Remplacer le vrai UserService par le mock
            { provide: UserService, useValue: mockUserService }
        ]
    }).compileComponents();

    fixture = TestBed.createComponent(UserListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
});

// Tester l'interaction avec le mock
it('should load users on init', () => {
    expect(mockUserService.getUsers).toHaveBeenCalled();
    expect(component.users.length).toBe(2);
});

it('should display user names', () => {
    const names = fixture.nativeElement.querySelectorAll('.user-name');
    expect(names[0].textContent).toContain('Alice');
    expect(names[1].textContent).toContain('Bob');
});
Avantage du mocking : Vous testez votre composant en isolation, sans dépendre d'un backend réel. Les tests s'exécutent en millisecondes et sont déterministes.

Tests des appels HTTP

Utilisez HttpTestingController pour intercepter et valider les requêtes HTTP sans les envoyer réellement :

// Importer HttpClientTestingModule
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

beforeEach(async () => {
    await TestBed.configureTestingModule({
        imports: [HttpClientTestingModule],
        declarations: [UserListComponent],
        providers: [UserService]
    }).compileComponents();

    fixture = TestBed.createComponent(UserListComponent);
    userService = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
});

// Tester l'appel HTTP
it('should fetch users from API', () => {
    const mockUsers = [
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' }
    ];

    userService.getUsers().subscribe(users => {
        expect(users.length).toBe(2);
        expect(users[0].name).toBe('Alice');
    });

    // Intercepter la requête GET /api/users
    const req = httpMock.expectOne('/api/users');
    expect(req.request.method).toBe('GET');

    // Simuler la réponse du serveur
    req.flush(mockUsers);
});

// Vérifier qu'aucune requête n'a été envoyée
afterEach(() => {
    httpMock.verify();  // Lance une erreur si des requêtes non consommées subsistent
});
Astuces :
  • httpMock.expectOne(url) pour une seule requête
  • httpMock.match() pour plusieurs requêtes à la même URL
  • req.error() pour simuler une erreur 500
  • httpMock.verify() en cleanup pour attraper les requêtes orphelines

Conclusion

Les tests unitaires sont un investissement dans la stabilité de votre code. Avec Jasmine et Karma (ou Vitest), vous pouvez tester chaque couche de votre application Angular : composants, services, directives, pipes.

Prochaines étapes :
  • Visez 80%+ de couverture de code (code coverage)
  • Testez les cas nominaux ET les cas d'erreur
  • Utilisez Spectator ou Testing Library pour des tests plus lisibles
  • Intégrez les tests dans votre CI/CD (GitLab CI, GitHub Actions, etc.)
  • Considérez Vitest pour une exécution plus rapide (Angular 17+)