Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Mocking - symulacja warunków bojowych

Witaj, mistrzu iluzji! Konsul Caesar.js potrzebuje Twojej pomocy w przygotowaniu legionu na wszystkie możliwe scenariusze, nie wystawiając go na prawdziwe niebezpieczeństwo. Czas nauczyć się mocking - sztuki symulowania różnych warunków i sytuacji w bezpiecznym środowisku!

Czym jest Mocking w świecie legionistów?

Mocking to jak symulacja warunków bojowych na poligonie. Zamiast czekać na prawdziwą bitwę, symulujemy ją w kontrolowanych warunkach:

  • Symulacja oblężenia zamiast czekania na prawdziwe
  • Imitacja ataku barbarzyńców bez rzeczywistego niebezpieczeństwa
  • Testowanie systemów bez wpływu na prawdziwe dane
  • Sprawdzanie reakcji legionu w różnych scenariuszach
1// Przykład - symulacja usługi pogodowej
2const mockWeatherService = {
3 getWeather: jest.fn().mockResolvedValue({
4 condition: 'storm',
5 windSpeed: 45,
6 waveHeight: 4.5,
7 recommendation: 'stay_in_port'
8 })
9};
10
11// Teraz możemy testować jak nasz fort reaguje na burzę
12// bez czekania na prawdziwą złą pogodę!

Rodzaje Mock Objects

1. Service Mocks - imitacja specjalistów

1// Prawdziwy serwis pogodowy
2@Injectable()
3export class WeatherService {
4 async getWeatherReport(coordinates: Coordinates): Promise<WeatherReport> {
5 // Prawdziwe wywołanie API pogodowego
6 const response = await fetch(`https://weather-api.com/marine/${coordinates.lat}/${coordinates.lng}`);
7 return response.json();
8 }
9}
10
11// Mock serwisu pogodowego
12const mockWeatherService = {
13 getWeatherReport: jest.fn().mockImplementation(async (coordinates: Coordinates) => {
14 // Symulujemy różne warunki pogodowe
15 if (coordinates.lat > 60) {
16 return {
17 condition: 'arctic_storm',
18 temperature: -20,
19 windSpeed: 60,
20 visibility: 'zero',
21 recommendation: 'extreme_danger'
22 };
23 }
24 
25 if (coordinates.lat < -60) {
26 return {
27 condition: 'antarctic_blizzard',
28 temperature: -40,
29 windSpeed: 80,
30 icebergs: true,
31 recommendation: 'turn_back_immediately'
32 };
33 }
34 
35 return {
36 condition: 'calm',
37 temperature: 25,
38 windSpeed: 10,
39 waveHeight: 1.5,
40 recommendation: 'perfect_marching'
41 };
42 })
43};
44
45// Test z mock serwisem
46describe('Navigation Planning', () => {
47 let navigationService: NavigationService;
48 
49 beforeEach(() => {
50 navigationService = new NavigationService(mockWeatherService);
51 });
52 
53 it('powinno odrzucić plan kampanii w przypadku burzy arktycznej', async () => {
54 const arcticCoordinates = { lat: 70, lng: -120 };
55 
56 const result = await navigationService.planVoyage(arcticCoordinates);
57 
58 expect(result.approved).toBe(false);
59 expect(result.reason).toContain('extreme weather conditions');
60 expect(mockWeatherService.getWeatherReport).toHaveBeenCalledWith(arcticCoordinates);
61 });
62 
63 it('powinno zatwierdzić plan kampanii w spokojnych warunkach', async () => {
64 const tropicalCoordinates = { lat: 25, lng: -80 };
65 
66 const result = await navigationService.planVoyage(tropicalCoordinates);
67 
68 expect(result.approved).toBe(true);
69 expect(result.estimatedDuration).toBeGreaterThan(0);
70 });
71});

2. Database Mocks - imitacja skarbnicy tributów

1// Mock repozytorium tributów
2const mockTributeRepository = {
3 find: jest.fn(),
4 findOne: jest.fn(),
5 save: jest.fn(),
6 delete: jest.fn(),
7 count: jest.fn(),
8 
9 // Preset responses dla różnych scenariuszy
10 mockEmptyVault: function() {
11 this.find.mockResolvedValue([]);
12 this.count.mockResolvedValue(0);
13 },
14 
15 mockFullVault: function() {
16 const tributes = [
17 { id: 1, name: 'Golden Coin', value: 100, rarity: 'common' },
18 { id: 2, name: 'Ruby Necklace', value: 1500, rarity: 'rare' },
19 { id: 3, name: 'Diamond Crown', value: 10000, rarity: 'legendary' }
20 ];
21 this.find.mockResolvedValue(tributes);
22 this.count.mockResolvedValue(tributes.length);
23 },
24 
25 mockDatabaseError: function() {
26 this.find.mockRejectedValue(new Error('Database connection lost'));
27 this.save.mockRejectedValue(new Error('Transaction failed'));
28 }
29};
30
31describe('Tribute Management', () => {
32 let tributeService: TributeService;
33 
34 beforeEach(() => {
35 jest.clearAllMocks();
36 tributeService = new TributeService(mockTributeRepository);
37 });
38 
39 it('powinno obsłużyć pustą skarbnicę tributów', async () => {
40 mockTributeRepository.mockEmptyVault();
41 
42 const tributes = await tributeService.getAllTributes();
43 const summary = await tributeService.getTreasurySummary();
44 
45 expect(tributes).toHaveLength(0);
46 expect(summary.totalValue).toBe(0);
47 expect(summary.message).toContain('vault is empty');
48 });
49 
50 it('powinno obsłużyć pełną skarbnicę tributów', async () => {
51 mockTributeRepository.mockFullVault();
52 
53 const summary = await tributeService.getTreasurySummary();
54 
55 expect(summary.totalValue).toBe(11600);
56 expect(summary.legendaryCount).toBe(1);
57 expect(summary.averageValue).toBe(3866.67);
58 });
59 
60 it('powinno obsłużyć błąd bazy danych', async () => {
61 mockTributeRepository.mockDatabaseError();
62 
63 await expect(tributeService.getAllTributes())
64 .rejects.toThrow('Database connection lost');
65 
66 await expect(tributeService.addTribute({ name: 'Test', value: 100 }))
67 .rejects.toThrow('Transaction failed');
68 });
69});

3. HTTP Service Mocks - imitacja komunikacji między fortami

1// Mock HttpService dla komunikacji z innymi fortami
2const mockHttpService = {
3 get: jest.fn(),
4 post: jest.fn(),
5 put: jest.fn(),
6 delete: jest.fn()
7};
8
9// Setup różnych scenariuszy komunikacji
10class CommunicationMockSetup {
11 static mockSuccessfulCommunication() {
12 mockHttpService.get.mockResolvedValue({
13 data: {
14 cohortName: 'Ultio Reginae',
15 centurion: 'EdDoctus',
16 response: 'Ave! We acknowledge your signal',
17 status: 'friendly'
18 },
19 status: 200
20 });
21 }
22 
23 static mockHostileLegion() {
24 mockHttpService.get.mockResolvedValue({
25 data: {
26 cohortName: 'Legio Velox',
27 centurion: 'Legate Norrington',
28 response: 'Stand down or be fired upon!',
29 status: 'hostile',
30 warningShots: 2
31 },
32 status: 200
33 });
34 }
35 
36 static mockNoResponse() {
37 mockHttpService.get.mockRejectedValue(new Error('No response from cohort'));
38 }
39 
40 static mockCommunicationJammed() {
41 mockHttpService.get.mockRejectedValue(new Error('Communication channels jammed'));
42 }
43}
44
45describe('Legion Communication', () => {
46 let communicationService: CommunicationService;
47 
48 beforeEach(() => {
49 jest.clearAllMocks();
50 communicationService = new CommunicationService(mockHttpService);
51 });
52 
53 it('powinno nawiązać komunikację z przyjaznym fortyem', async () => {
54 CommunicationMockSetup.mockSuccessfulCommunication();
55 
56 const result = await communicationService.contactLegion({
57 coordinates: { lat: 25.7, lng: -80.1 },
58 frequency: '156.800'
59 });
60 
61 expect(result.status).toBe('successful');
62 expect(result.cohortStatus).toBe('friendly');
63 expect(result.recommendation).toBe('safe_to_approach');
64 });
65 
66 it('powinno wykryć wrogie intencje', async () => {
67 CommunicationMockSetup.mockHostileLegion();
68 
69 const result = await communicationService.contactLegion({
70 coordinates: { lat: 30.0, lng: -85.0 },
71 frequency: '156.800'
72 });
73 
74 expect(result.status).toBe('successful');
75 expect(result.cohortStatus).toBe('hostile');
76 expect(result.recommendation).toBe('maintain_distance');
77 expect(result.threatLevel).toBe('high');
78 });
79 
80 it('powinno obsłużyć brak odpowiedzi', async () => {
81 CommunicationMockSetup.mockNoResponse();
82 
83 const result = await communicationService.contactLegion({
84 coordinates: { lat: 0, lng: 0 },
85 frequency: '156.800'
86 });
87 
88 expect(result.status).toBe('failed');
89 expect(result.reason).toContain('No response');
90 expect(result.recommendation).toBe('proceed_with_caution');
91 });
92});

4. Time Mocks - kontrola czasu

1// Mock Date i timers dla testów czasowych
2describe('Expedition Timing', () => {
3 let expeditionService: ExpeditionService;
4 
5 beforeEach(() => {
6 // Mock czasu na stały moment
7 jest.useFakeTimers();
8 jest.setSystemTime(new Date('2023-06-15T08:00:00Z'));
9 
10 expeditionService = new ExpeditionService();
11 });
12 
13 afterEach(() => {
14 jest.useRealTimers();
15 });
16 
17 it('powinno zaplanować wyprawę na odpowiedni czas', async () => {
18 const expeditionPlan = {
19 departureTime: new Date('2023-06-15T10:00:00Z'),
20 duration: 7 * 24 * 60 * 60 * 1000, // 7 dni w ms
21 destination: 'Tribute Island'
22 };
23 
24 const result = await expeditionService.scheduleExpedition(expeditionPlan);
25 
26 expect(result.departureTime).toEqual(new Date('2023-06-15T10:00:00Z'));
27 expect(result.expectedReturn).toEqual(new Date('2023-06-22T10:00:00Z'));
28 expect(result.preparationTime).toBe(2 * 60 * 60 * 1000); // 2 godziny na przygotowania
29 });
30 
31 it('powinno obsłużyć przekroczenie czasu wyprawy', async () => {
32 // Rozpocznij wyprawę
33 const expedition = await expeditionService.startExpedition({
34 id: 'test-expedition',
35 duration: 60 * 60 * 1000 // 1 godzina
36 });
37 
38 expect(expedition.status).toBe('in_progress');
39 
40 // Przesuń czas o 2 godziny
41 jest.advanceTimersByTime(2 * 60 * 60 * 1000);
42 
43 // Sprawdź status wyprawy
44 const status = await expeditionService.checkExpeditionStatus('test-expedition');
45 
46 expect(status.isOverdue).toBe(true);
47 expect(status.overdueBy).toBe(60 * 60 * 1000); // 1 godzina spóźnienia
48 expect(status.recommendation).toBe('send_rescue_mission');
49 });
50});

5. Complex Scenario Mocks - złożone scenariusze

1// Mock manager dla złożonych scenariuszy
2class BattleScenarioMocks {
3 static setupEpicSeaBattle() {
4 const mockEnemyLegion = {
5 cohorts: [
6 { name: 'Legio Victrix', ballistae: 40, health: 100, position: { x: 100, y: 200 } },
7 { name: 'Legio Audax', ballistae: 32, health: 90, position: { x: 150, y: 180 } },
8 { name: 'Legio Ultrix', ballistae: 28, health: 85, position: { x: 80, y: 220 } }
9 ],
10 formation: 'line_of_battle',
11 commander: 'Legate Nelson',
12 morale: 'high'
13 };
14 
15 const mockWeatherConditions = {
16 visibility: 'poor',
17 windDirection: 'northwest',
18 windSpeed: 25,
19 waveHeight: 3.0,
20 timeOfDay: 'dawn'
21 };
22 
23 const mockBattleEvents = [
24 { time: 0, event: 'battle_begins', description: 'Enemy legion spotted on horizon' },
25 { time: 300, event: 'first_volley', result: 'enemy_hit', damage: 15 },
26 { time: 600, event: 'return_fire', result: 'friendly_hit', damage: 20 },
27 { time: 900, event: 'weather_change', change: 'fog_rolls_in' },
28 { time: 1200, event: 'critical_hit', target: 'enemy_praetoria', damage: 40 },
29 { time: 1500, event: 'enemy_retreat', reason: 'heavy_casualties' }
30 ];
31 
32 return {
33 enemyLegion: mockEnemyLegion,
34 weather: mockWeatherConditions,
35 events: mockBattleEvents
36 };
37 }
38}
39
40describe('Epic Sea Battle', () => {
41 let battleService: BattleService;
42 let mockEventSystem: jest.Mocked<EventSystem>;
43 
44 beforeEach(() => {
45 mockEventSystem = {
46 emit: jest.fn(),
47 on: jest.fn(),
48 removeListener: jest.fn()
49 };
50 
51 battleService = new BattleService(mockEventSystem);
52 });
53 
54 it('powinno przeprowadzić epicką bitwę', async () => {
55 const scenario = BattleScenarioMocks.setupEpicSeaBattle();
56 
57 // Mock responses dla różnych faz bitwy
58 mockEventSystem.emit
59 .mockReturnValueOnce(true) // battle_begins
60 .mockReturnValueOnce(true) // first_volley
61 .mockReturnValueOnce(true) // return_fire
62 .mockReturnValueOnce(true) // weather_change
63 .mockReturnValueOnce(true) // critical_hit
64 .mockReturnValueOnce(true); // enemy_retreat
65 
66 const result = await battleService.simulateBattle({
67 playerLegion: {
68 cohorts: [{ name: 'Equus Niger', ballistae: 32, health: 100 }],
69 commander: 'Centurion Caesar'
70 },
71 enemyLegion: scenario.enemyLegion,
72 weather: scenario.weather,
73 events: scenario.events
74 });
75 
76 // Sprawdź wyniki bitwy
77 expect(result.outcome).toBe('victory');
78 expect(result.playerLosses).toBeLessThan(50);
79 expect(result.enemyLosses).toBeGreaterThan(70);
80 expect(result.battleDuration).toBe(1500); // 25 minut
81 expect(result.decisiveEvent).toBe('critical_hit');
82 
83 // Sprawdź czy eventy zostały wywołane w odpowiedniej kolejności
84 expect(mockEventSystem.emit).toHaveBeenCalledTimes(6);
85 expect(mockEventSystem.emit).toHaveBeenNthCalledWith(
86 1, 'battle_event', expect.objectContaining({ event: 'battle_begins' })
87 );
88 expect(mockEventSystem.emit).toHaveBeenLastCalledWith(
89 'battle_event', expect.objectContaining({ event: 'enemy_retreat' })
90 );
91 });
92});

6. Mock Factories - fabryki mock obiektów

1// Fabryka do tworzenia mock obiektów
2class LegionaryMockFactory {
3 static createMockLegionary(overrides: Partial<Legionary> = {}): Legionary {
4 return {
5 id: Math.random().toString(36),
6 name: 'Test Legionary',
7 rank: 'Legionary',
8 experience: 100,
9 skills: ['Marching', 'Combat'],
10 health: 100,
11 morale: 80,
12 equipment: ['Gladius', 'Pilum'],
13 ...overrides
14 };
15 }
16 
17 static createMockLegion(size: number, ranks: string[] = []): Legionary[] {
18 return Array.from({ length: size }, (_, index) => {
19 const rank = ranks[index] || (index === 0 ? 'Centurion' : 'Legionary');
20 return this.createMockLegionary({
21 name: `Legionary ${index + 1}`,
22 rank,
23 experience: 50 + (index * 25)
24 });
25 });
26 }
27 
28 static createMockLegion(legionSize: number = 20): Legion {
29 return {
30 id: Math.random().toString(36),
31 name: 'Test Legion',
32 type: 'Cohors',
33 health: 100,
34 ballistae: 24,
35 speed: 12,
36 capacity: 50,
37 legion: this.createMockLegion(legionSize),
38 supplies: {
39 food: 30,
40 water: 25,
41 ammunition: 500,
42 wine: 100
43 }
44 };
45 }
46}
47
48// Użycie mock factory
49describe('Legion Operations', () => {
50 it('powinno zarządzać legionem różnych oddziałów', () => {
51 const legate = LegionaryMockFactory.createMockLegionary({
52 name: 'Legate Caesar',
53 rank: 'Legate',
54 experience: 2000
55 });
56 
57 const legion = [
58 LegionaryMockFactory.createMockLegion(30), // Duży fort flagowy
59 LegionaryMockFactory.createMockLegion(20), // Średni fort
60 LegionaryMockFactory.createMockLegion(15), // Mały fort zwiadowczy
61 ];
62 
63 const legionManager = new LegionManager(legate, legion);
64 
65 expect(legionManager.getTotalFirepower()).toBeGreaterThan(60);
66 expect(legionManager.getTotalLegion()).toBe(65);
67 expect(legionManager.isReadyForExpedition()).toBe(true);
68 });
69});

Najlepsze praktyki Mocking

1. Mock Cleanup

1describe('Service Tests', () => {
2 afterEach(() => {
3 jest.clearAllMocks(); // Wyczyść wszystkie mock calls
4 jest.restoreAllMocks(); // Przywróć oryginalne implementacje
5 });
6});

2. Realistic Mock Data

1// ✅ Dobry mock - realistyczne dane
2const goodMockWeather = {
3 temperature: 23,
4 windSpeed: 15,
5 waveHeight: 2.1,
6 visibility: 'good',
7 humidity: 78
8};
9
10// ❌ Zły mock - nierealistyczne dane
11const badMockWeather = {
12 temperature: 999,
13 windSpeed: -50,
14 waveHeight: 'very big',
15 visibility: true
16};

3. Verify Mock Interactions

1it('powinno wywołać serwis pogodowy z prawidłowymi parametrami', async () => {
2 await navigationService.planRoute(startPoint, endPoint);
3 
4 expect(mockWeatherService.getWeather).toHaveBeenCalledTimes(2);
5 expect(mockWeatherService.getWeather).toHaveBeenNthCalledWith(1, startPoint);
6 expect(mockWeatherService.getWeather).toHaveBeenNthCalledWith(2, endPoint);
7});

Mocking to sztuka przygotowania legionu na wszystkie możliwe scenariusze bez wystawiania go na prawdziwe niebezpieczeństwo. Dobrze przygotowane mocki pozwalają testować nawet najbardziej ekstremalne warunki w bezpiecznym środowisku!

Przejdź do CodeWorlds