Kurs Angular · Moduł 11: Testowanie
Mocki i Spy - Techniki Podstępu
W tej lekcji4
Mocki to jak posłańcy ninja - udają prawdziwe obiekty, ale są pod kontrolą testów.
jasmine.createSpyObj
describe('DojoComponent with Mock', () => {
let component: DojoComponent;
let fixture: ComponentFixture<DojoComponent>;
let samuraiServiceSpy: jasmine.SpyObj<SamuraiService>;
beforeEach(async () => {
// Tworzymy mock z metodami
samuraiServiceSpy = jasmine.createSpyObj('SamuraiService', [
'getAll',
'getById',
'create',
'update'
]);
// Ustawiamy domyślne zwracane wartości
samuraiServiceSpy.getAll.and.returnValue(of([
{ id: 1, name: 'Musashi' }
]));
await TestBed.configureTestingModule({
imports: [DojoComponent],
providers: [
{ provide: SamuraiService, useValue: samuraiServiceSpy }
]
}).compileComponents();
fixture = TestBed.createComponent(DojoComponent);
component = fixture.componentInstance;
});
it('should load samurai on init', () => {
fixture.detectChanges(); // ngOnInit
expect(samuraiServiceSpy.getAll).toHaveBeenCalled();
expect(component.samuraiList.length).toBe(1);
});
it('should call create when adding samurai', () => {
const newSamurai = { name: 'Hanzo', clan: 'Iga' };
samuraiServiceSpy.create.and.returnValue(of({ id: 2, ...newSamurai }));
component.addSamurai(newSamurai);
expect(samuraiServiceSpy.create).toHaveBeenCalledWith(newSamurai);
});
});spyOn - Szpiegowanie Metod
describe('BattleService', () => {
let service: BattleService;
let loggerService: LoggerService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [BattleService, LoggerService]
});
service = TestBed.inject(BattleService);
loggerService = TestBed.inject(LoggerService);
});
it('should log battle result', () => {
// Szpiegujemy istniejącą metodę
const logSpy = spyOn(loggerService, 'log');
service.fight('Musashi', 'Sasaki');
expect(logSpy).toHaveBeenCalledWith(
jasmine.stringMatching(/Battle:/)
);
});
it('should call through and track', () => {
// Wywołaj prawdziwą metodę, ale też śledź
spyOn(loggerService, 'log').and.callThrough();
service.fight('Musashi', 'Sasaki');
expect(loggerService.log).toHaveBeenCalled();
});
it('should fake implementation', () => {
spyOn(loggerService, 'log').and.callFake((message) => {
console.log('FAKE LOG:', message);
});
service.fight('Musashi', 'Sasaki');
});
});Mock Class
// mock-samurai.service.ts
export class MockSamuraiService {
private mockData: Samurai[] = [
{ id: 1, name: 'Test Samurai', clan: 'Test Clan', level: 1 }
];
getAll(): Observable<Samurai[]> {
return of(this.mockData);
}
getById(id: number): Observable<Samurai | undefined> {
return of(this.mockData.find(s => s.id === id));
}
create(samurai: Omit<Samurai, 'id'>): Observable<Samurai> {
const newSamurai = { id: Date.now(), ...samurai };
this.mockData.push(newSamurai);
return of(newSamurai);
}
}
// Użycie w teście
TestBed.configureTestingModule({
providers: [
{ provide: SamuraiService, useClass: MockSamuraiService }
]
});Testowanie Zależności z InjectionToken
import { InjectionToken } from '@angular/core';
export const API_CONFIG = new InjectionToken<ApiConfig>('api.config');
describe('ConfigurableService', () => {
it('should use injected config', () => {
const mockConfig: ApiConfig = {
baseUrl: 'http://test-api',
timeout: 1000
};
TestBed.configureTestingModule({
providers: [
ConfigurableService,
{ provide: API_CONFIG, useValue: mockConfig }
]
});
const service = TestBed.inject(ConfigurableService);
expect(service.getBaseUrl()).toBe('http://test-api');
});
});Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jaka jest różnica między jasmine.createSpyObj a spyOn?
2. Jak ustawić wartość zwracaną przez metodę mocka utworzonego przez createSpyObj?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Edytor kodu
Uzupełnij test komponentu z zamockowanym serwisem.
- Układanie w pionie
Uporządkuj kroki przygotowania zamockowanego serwisu w beforeEach (od góry do dołu):
- Klikanie w kolejności
Ułóż linię szpiegującą metodę log istniejącego serwisu: spyOn(loggerService, 'log')
- Układanie w poziomie
Ułóż provider podmieniający serwis na klasę mock (jedna linia):