Cours Angular · Module 11: Tests

Mocks et spies - Techniques de tromperie

2 min de lecture
Dans cette leçon4

Les mocks sont comme des messagers ninja : ils se font passer pour de vrais objets, mais restent sous le contrôle des tests.

jasmine.createSpyObj

1describe('DojoComponent with Mock', () => {
2  let component: DojoComponent;
3  let fixture: ComponentFixture<DojoComponent>;
4  let samuraiServiceSpy: jasmine.SpyObj<SamuraiService>;
5
6  beforeEach(async () => {
7    // Créer un mock avec des méthodes
8    samuraiServiceSpy = jasmine.createSpyObj('SamuraiService', [
9      'getAll',
10      'getById',
11      'create',
12      'update'
13    ]);
14
15    // Définir les valeurs de retour par défaut
16    samuraiServiceSpy.getAll.and.returnValue(of([
17      { id: 1, name: 'Musashi' }
18    ]));
19
20    await TestBed.configureTestingModule({
21      imports: [DojoComponent],
22      providers: [
23        { provide: SamuraiService, useValue: samuraiServiceSpy }
24      ]
25    }).compileComponents();
26
27    fixture = TestBed.createComponent(DojoComponent);
28    component = fixture.componentInstance;
29  });
30
31  it('should load samurai on init', () => {
32    fixture.detectChanges();  // ngOnInit
33
34    expect(samuraiServiceSpy.getAll).toHaveBeenCalled();
35    expect(component.samuraiList.length).toBe(1);
36  });
37
38  it('should call create when adding samurai', () => {
39    const newSamurai = { name: 'Hanzo', clan: 'Iga' };
40    samuraiServiceSpy.create.and.returnValue(of({ id: 2, ...newSamurai }));
41
42    component.addSamurai(newSamurai);
43
44    expect(samuraiServiceSpy.create).toHaveBeenCalledWith(newSamurai);
45  });
46});

spyOn - Espionner des méthodes

1describe('BattleService', () => {
2  let service: BattleService;
3  let loggerService: LoggerService;
4
5  beforeEach(() => {
6    TestBed.configureTestingModule({
7      providers: [BattleService, LoggerService]
8    });
9
10    service = TestBed.inject(BattleService);
11    loggerService = TestBed.inject(LoggerService);
12  });
13
14  it('should log battle result', () => {
15    // Espionner une méthode existante
16    const logSpy = spyOn(loggerService, 'log');
17
18    service.fight('Musashi', 'Sasaki');
19
20    expect(logSpy).toHaveBeenCalledWith(
21      jasmine.stringMatching(/Battle:/)
22    );
23  });
24
25  it('should call through and track', () => {
26    // Appeler la vraie méthode, tout en la suivant
27    spyOn(loggerService, 'log').and.callThrough();
28
29    service.fight('Musashi', 'Sasaki');
30
31    expect(loggerService.log).toHaveBeenCalled();
32  });
33
34  it('should fake implementation', () => {
35    spyOn(loggerService, 'log').and.callFake((message) => {
36      console.log('FAKE LOG:', message);
37    });
38
39    service.fight('Musashi', 'Sasaki');
40  });
41});

Classe mock

1// mock-samurai.service.ts
2export class MockSamuraiService {
3  private mockData: Samurai[] = [
4    { id: 1, name: 'Test Samurai', clan: 'Test Clan', level: 1 }
5  ];
6
7  getAll(): Observable<Samurai[]> {
8    return of(this.mockData);
9  }
10
11  getById(id: number): Observable<Samurai | undefined> {
12    return of(this.mockData.find(s => s.id === id));
13  }
14
15  create(samurai: Omit<Samurai, 'id'>): Observable<Samurai> {
16    const newSamurai = { id: Date.now(), ...samurai };
17    this.mockData.push(newSamurai);
18    return of(newSamurai);
19  }
20}
21
22// Utilisation dans les tests
23TestBed.configureTestingModule({
24  providers: [
25    { provide: SamuraiService, useClass: MockSamuraiService }
26  ]
27});

Tester les dépendances avec InjectionToken

1import { InjectionToken } from '@angular/core';
2
3export const API_CONFIG = new InjectionToken<ApiConfig>('api.config');
4
5describe('ConfigurableService', () => {
6  it('should use injected config', () => {
7    const mockConfig: ApiConfig = {
8      baseUrl: 'http://test-api',
9      timeout: 1000
10    };
11
12    TestBed.configureTestingModule({
13      providers: [
14        ConfigurableService,
15        { provide: API_CONFIG, useValue: mockConfig }
16      ]
17    });
18
19    const service = TestBed.inject(ConfigurableService);
20    expect(service.getBaseUrl()).toBe('http://test-api');
21  });
22});

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Quelle est la différence entre jasmine.createSpyObj et spyOn ?

  2. 2. Comment définis-tu la valeur de retour d'une méthode mock créée avec createSpyObj ?

Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Complète le test de composant avec un service mocké.

  • Tri vertical

    Ordonne les étapes de préparation d'un service mocké dans beforeEach (de haut en bas) :

  • Clique dans l'ordre

    Ordonne la ligne qui espionne la méthode log d'un service existant : spyOn(loggerService, 'log')

  • Tri horizontal

    Ordonne le provider qui remplace le service par une classe mock (une seule ligne) :

Articles utiles