Mocks are like ninja messengers - they impersonate real objects but are under the control of tests.
1describe('DojoComponent with Mock', () => {
2 let component: DojoComponent;
3 let fixture: ComponentFixture<DojoComponent>;
4 let samuraiServiceSpy: jasmine.SpyObj<SamuraiService>;
5
6 beforeEach(async () => {
7 // Create mock with methods
8 samuraiServiceSpy = jasmine.createSpyObj('SamuraiService', [
9 'getAll',
10 'getById',
11 'create',
12 'update'
13 ]);
14
15 // Set default return values
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});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 // Spy on an existing method
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 // Call the real method, but also track it
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});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// Usage in tests
23TestBed.configureTestingModule({
24 providers: [
25 { provide: SamuraiService, useClass: MockSamuraiService }
26 ]
27});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});