Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Testowanie kodu asynchronicznego

W Parku Jurajskim wiele operacji jest asynchronicznych - skanowanie DNA, komunikacja z sensorami, pobieranie danych z satelity. Jest oferuje kilka sposobów testowania takiego kodu.

Testowanie Promise

async/await

Najbardziej czytelny sposób testowania asynchronicznego kodu:

1// Funkcja asynchroniczna do testowania
2async function scanDNA(sampleId) {
3  const response = await fetch(`/api/dna/${sampleId}`);
4  const data = await response.json();
5  return {
6    species: data.species,
7    purity: data.purity,
8    viable: data.purity > 0.9
9  };
10}
11
12// Test z async/await
13test('skanowanie DNA zwraca gatunek', async () => {
14  const result = await scanDNA('SAMPLE-001');
15
16  expect(result.species).toBe('T-Rex');
17  expect(result.purity).toBeGreaterThan(0.9);
18  expect(result.viable).toBe(true);
19});

resolves i rejects

Jest posiada dedykowane matchery dla Promise:

1// Testowanie resolve
2test('DNA scan resolves with data', async () => {
3  await expect(scanDNA('SAMPLE-001')).resolves.toEqual({
4    species: 'T-Rex',
5    purity: 0.95,
6    viable: true
7  });
8});
9
10// Testowanie reject
11test('DNA scan rejects for invalid sample', async () => {
12  await expect(scanDNA('INVALID')).rejects.toThrow('Sample not found');
13});
14
15// Inny przykład - testowanie odrzuconej Promise
16async function activateDefense(zoneId) {
17  if (!zoneId) {
18    throw new Error('Zone ID is required');
19  }
20  return { status: 'activated', zone: zoneId };
21}
22
23test('defense rejects without zone ID', async () => {
24  await expect(activateDefense(null)).rejects.toThrow('Zone ID is required');
25});
26
27test('defense resolves with valid zone', async () => {
28  await expect(activateDefense('A')).resolves.toEqual({
29    status: 'activated',
30    zone: 'A'
31  });
32});

Testowanie callbacków

1function fetchDinoReport(id, callback) {
2  setTimeout(() => {
3    if (id === 'unknown') {
4      callback(new Error('Dinosaur not found'), null);
5    } else {
6      callback(null, { id, status: 'healthy' });
7    }
8  }, 100);
9}
10
11// Test callbacku z done
12test('callback zwraca raport dinozaura', (done) => {
13  fetchDinoReport('REX-001', (error, report) => {
14    try {
15      expect(error).toBeNull();
16      expect(report.status).toBe('healthy');
17      done(); // Sygnalizuj zakończenie testu
18    } catch (err) {
19      done(err); // Przekaż błąd do Jest
20    }
21  });
22});

Fake Timers

Gdy kod używa

setTimeout
,
setInterval
lub
Date
, możesz kontrolować czas:

1// Funkcja z timeoutem
2function startEmergencyCountdown(callback) {
3  console.log('Ewakuacja za 30 sekund!');
4  setTimeout(() => {
5    callback('Ewakuacja rozpoczęta!');
6  }, 30000); // 30 sekund
7}
8
9describe('Emergency System', () => {
10  beforeEach(() => {
11    jest.useFakeTimers(); // Włącz sztuczne timery
12  });
13
14  afterEach(() => {
15    jest.useRealTimers(); // Przywróć prawdziwe timery
16  });
17
18  test('countdown wywołuje callback po 30s', () => {
19    const callback = jest.fn();
20
21    startEmergencyCountdown(callback);
22
23    // Callback jeszcze nie został wywołany
24    expect(callback).not.toHaveBeenCalled();
25
26    // Przewiń czas o 30 sekund
27    jest.advanceTimersByTime(30000);
28
29    // Teraz callback został wywołany!
30    expect(callback).toHaveBeenCalledWith('Ewakuacja rozpoczęta!');
31  });
32
33  test('countdown nie odpala się przed czasem', () => {
34    const callback = jest.fn();
35
36    startEmergencyCountdown(callback);
37
38    // Przewiń o 29 sekund
39    jest.advanceTimersByTime(29000);
40    expect(callback).not.toHaveBeenCalled();
41
42    // Przewiń o kolejną sekundę (łącznie 30s)
43    jest.advanceTimersByTime(1000);
44    expect(callback).toHaveBeenCalled();
45  });
46});

Uruchamianie oczekujących timerów

1test('runAllTimers - uruchom wszystkie timery', () => {
2  const steps = [];
3
4  setTimeout(() => steps.push('krok 1'), 1000);
5  setTimeout(() => steps.push('krok 2'), 2000);
6  setTimeout(() => steps.push('krok 3'), 5000);
7
8  jest.useFakeTimers();
9
10  // Uruchom WSZYSTKIE oczekujące timery naraz
11  jest.runAllTimers();
12
13  expect(steps).toEqual(['krok 1', 'krok 2', 'krok 3']);
14
15  jest.useRealTimers();
16});
Ir a CodeWorlds