W Parku Jurajskim wiele operacji jest asynchronicznych - skanowanie DNA, komunikacja z sensorami, pobieranie danych z satelity. Jest oferuje kilka sposobów testowania takiego kodu.
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});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});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});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});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});