Aller au contenu principal

Lab 22 : Tests unitaires avec Vitest

📖 Ressources​

🚀 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​

É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 jsdom

Cré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​

npm test

Vérifiez que tous les tests existants passent avant d'en ajouter de nouveaux.

É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​

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​

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​

npm run test:coverage

Consultez le rapport de couverture et assurez-vous que les méthodes critiques des services sont couvertes.

Étape 7 : Ouvrir l'interface graphique​

npx vitest --ui

Explorez le navigateur de tests interactif pour naviguer dans les tests et voir les résultats en temps réel.