Lab 22 : Tests unitaires avec Vitest
📖 Ressources
Section intitulée « 📖 Ressources »🚀 Code de départ
Section intitulée « 🚀 Code de départ »album-wholesale-v17-signal-pre-jest
Dans ce lab, vous écrirez des tests unitaires pour les services et les composants en utilisant Vitest, le test runner moderne recommandé pour Angular. Vous utiliserez vi.fn() pour les mocks, les matchers Vitest et les hooks de cycle de vie.
📝 Instructions
Section intitulée « 📝 Instructions »Étape 1 : Ajouter Vitest au projet
Section intitulée « Étape 1 : Ajouter Vitest au projet »Installez les versions compatibles manuellement (ng add @angular/vitest installe Vitest 4.x qui est incompatible avec Angular 17 / Vite 5) :
npm install --save-dev vitest@2 @vitest/coverage-v8@2 @analogjs/vite-plugin-angular jsdomCréez vite.config.ts à la racine du projet :
import { defineConfig } from 'vitest/config';import angular from '@analogjs/vite-plugin-angular';
export default defineConfig({ plugins: [angular()], test: { globals: true, environment: 'jsdom', include: ['**/*.spec.ts'], coverage: { provider: 'v8', reporter: ['text', 'html'], }, },});Ajoutez un script de test dans package.json :
{ "scripts": { "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage" }}Étape 2 : Exécuter les tests existants
Section intitulée « Étape 2 : Exécuter les tests existants »npm testVérifiez que tous les tests existants passent avant d’en ajouter de nouveaux.
Étape 3 : Tester un service
Section intitulée « Étape 3 : Tester un service »Créez src/app/services/album.service.spec.ts :
import { TestBed } from '@angular/core/testing';import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';import { AlbumService } from './album.service';import { Album } from '../model/album.model';
describe('AlbumService', () => { let service: AlbumService; let httpMock: HttpTestingController;
beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [AlbumService], }); service = TestBed.inject(AlbumService); httpMock = TestBed.inject(HttpTestingController); });
afterEach(() => httpMock.verify());
it('should fetch albums', () => { const mockAlbums: Album[] = [ { id: 1, name: 'Test Album', artist: 'Artist', price: 9, description: '', tags: [] }, ];
service.findAll().subscribe(albums => { expect(albums).toHaveLength(1); expect(albums[0].name).toBe('Test Album'); });
const req = httpMock.expectOne('http://localhost:3000/albums'); expect(req.request.method).toBe('GET'); req.flush(mockAlbums); });});Étape 4 : Utiliser vi.fn() pour mocker une dépendance
Section intitulée « Étape 4 : Utiliser vi.fn() pour mocker une dépendance »Créez src/app/components/album-list/album-list.component.spec.ts :
import { ComponentFixture, TestBed } from '@angular/core/testing';import { vi } from 'vitest';import { of } from 'rxjs';import { AlbumListComponent } from './album-list.component';import { AlbumService } from '../../services/album.service';
describe('AlbumListComponent', () => { let fixture: ComponentFixture<AlbumListComponent>; let albumServiceMock: { findAll: ReturnType<typeof vi.fn> };
beforeEach(async () => { albumServiceMock = { findAll: vi.fn().mockReturnValue(of([ { id: 1, name: 'Mock Album', artist: 'Mock Artist', price: 9, description: '', tags: [] }, ])), };
await TestBed.configureTestingModule({ imports: [AlbumListComponent], providers: [ { provide: AlbumService, useValue: albumServiceMock }, ], }).compileComponents();
fixture = TestBed.createComponent(AlbumListComponent); fixture.detectChanges(); });
it('should display albums', () => { const items = fixture.nativeElement.querySelectorAll('.album-card'); expect(items.length).toBeGreaterThan(0); });
it('should call findAll on init', () => { expect(albumServiceMock.findAll).toHaveBeenCalledOnce(); });});Étape 5 : Utiliser les hooks de cycle de vie
Section intitulée « Étape 5 : Utiliser les hooks de cycle de vie »Vitest fournit beforeAll, beforeEach, afterEach, afterAll :
describe('Calculs du panier', () => { let items: CartItem[];
beforeEach(() => { items = [ { id: 1, quantity: 2, price: 10 }, { id: 2, quantity: 1, price: 20 }, ]; });
it('should calculate total', () => { const total = items.reduce((sum, i) => sum + i.quantity * i.price, 0); expect(total).toBe(40); });
it('should count items', () => { expect(items).toHaveLength(2); });});Étape 6 : Exécuter avec la couverture
Section intitulée « Étape 6 : Exécuter avec la couverture »npm run test:coverageConsultez le rapport de couverture et assurez-vous que les méthodes critiques des services sont couvertes.
Étape 7 : Ouvrir l’interface graphique
Section intitulée « Étape 7 : Ouvrir l’interface graphique »npx vitest --uiExplorez le navigateur de tests interactif pour naviguer dans les tests et voir les résultats en temps réel.

