Skip to content
intermediatePhase 40 · Testing

Testing Async Code

Test asynchronous operations with proper assertions and timeouts.

30m
0 problems
Topic Progress0%

Async/Await in Tests

Async/Await in Tests

Basic Async Test

// Async function
describe('fetchUser', () => {
  it('should return user data', async () => {
    const user = await fetchUser(1);
    expect(user).toEqual({ id: 1, name: 'John' });
  });
});

Async Component Test

import { render, screen, waitFor } from '@testing-library/react';

describe('UserProfile', () => {
  it('should display user name after loading', async () => {
    render(<UserProfile id={1} />);

    // Wait for loading to complete
    await waitFor(() => {
      expect(screen.getByText('John')).toBeInTheDocument();
    });
  });

  it('should show error on failure', async () => {
    // Mock API error
    server.use(
      http.get('/api/users/1', () => {
        return HttpResponse.json(null, { status: 500 });
      })
    );

    render(<UserProfile id={1} />);

    await waitFor(() => {
      expect(screen.getByRole('alert')).toHaveTextContent('Error');
    });
  });
});

findBy Query

// findBy waits for element automatically
describe('SearchResults', () => {
  it('should display results', async () => {
    render(<SearchResults query="react" />);

    // findByText waits for element to appear
    const result = await screen.findByText('React Results');
    expect(result).toBeInTheDocument();
  });
});

Timer Mocks

Timer Mocks

Jest Fake Timers

// Use fake timers
beforeEach(() => {
  jest.useFakeTimers();
});

afterEach(() => {
  jest.useRealTimers();
});

describe('debounce', () => {
  it('should delay execution', () => {
    const fn = jest.fn();
    const debounced = debounce(fn, 300);

    debounced();
    expect(fn).not.toHaveBeenCalled();

    // Fast forward time
    jest.advanceTimersByTime(300);
    expect(fn).toHaveBeenCalledTimes(1);
  });
});

Async Timer Tests

describe('delay', () => {
  it('should resolve after delay', async () => {
    const promise = delay(1000);

    jest.advanceTimersByTime(1000);

    await expect(promise).resolves.toBeUndefined();
  });
});

Interval Testing

describe('polling', () => {
  it('should call function repeatedly', () => {
    const fn = jest.fn();
    startPolling(fn, 5000);

    jest.advanceTimersByTime(15000);

    expect(fn).toHaveBeenCalledTimes(3);
  });
});

Date Mocking

// Mock specific date
const mockDate = new Date('2024-01-15T12:00:00');
jest.spyOn(global, 'Date').mockImplementation((arg) => {
  if (arg) return new originalDate(arg);
  return mockDate;
});

// Or use jest.mock
dateSpy.mockReturnValue(mockDate);

Animation Testing

// Mock requestAnimationFrame
window.requestAnimationFrame = jest.fn(cb => setTimeout(cb, 16));
window.cancelAnimationFrame = jest.fn(id => clearTimeout(id));

// Mock CSS animations
Element.prototype.animate = jest.fn(() => ({
  finished: Promise.resolve(),
}));

Network Requests

Network Requests

MSW (Recommended)

import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  http.get('/api/data', () => {
    return HttpResponse.json({ value: 42 });
  })
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

it('should fetch data', async () => {
  render(<DataComponent />);

  await waitFor(() => {
    expect(screen.getByText('42')).toBeInTheDocument();
  });
});

Axios Mock

import axios from 'axios';
jest.mock('axios');

it('should fetch data with axios', async () => {
  axios.get.mockResolvedValueOnce({
    data: { id: 1, name: 'John' },
  });

  const user = await fetchUser(1);

  expect(user).toEqual({ id: 1, name: 'John' });
  expect(axios.get).toHaveBeenCalledWith('/api/users/1');
});

Error Testing

it('should handle network error', async () => {
  server.use(
    http.get('/api/data', () => {
      return HttpResponse.error();
    })
  );

  render(<DataComponent />);

  await waitFor(() => {
    expect(screen.getByText('Network error')).toBeInTheDocument();
  });
});

it('should handle timeout', async () => {
  server.use(
    http.get('/api/data', async () => {
      await delay(5000);
      return HttpResponse.json({ data: 'test' });
    })
  );

  render(<DataComponent />);

  await waitFor(() => {
    expect(screen.getByText('Request timeout')).toBeInTheDocument();
  });
});

Request Verification

it('should send correct request', async () => {
  const handler = http.post('/api/users', async ({ request }) => {
    const body = await request.json();
    expect(body).toEqual({ name: 'John', email: 'john@example.com' });
    return HttpResponse.json({ id: 1, ...body });
  });

  server.use(handler);

  render(<CreateUserForm />);

  await userEvent.type(screen.getByLabelText('Name'), 'John');
  await userEvent.type(screen.getByLabelText('Email'), 'john@example.com');
  await userEvent.click(screen.getByText('Create'));

  await waitFor(() => {
    expect(screen.getByText('User created')).toBeInTheDocument();
  });
});

Practice Problems

0/3solved
Build Testing Async Code Component

Create a reusable React component implementing Testing Async Code. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Testing Async Code Testing

Write unit and integration tests for Testing Async Code using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Testing Async Code Performance

Optimize Testing Async Code for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. How do you test async functions?

Question 1 options

2. What does jest.useFakeTimers do?

Question 2 options

3. What is the primary purpose of Testing Async Code?

Question 3 options

4. What is a common mistake when implementing Testing Async Code?

Question 4 options

Flashcards

Question

How do you test async code?

Answer

Use async/await, waitFor for assertions, and findBy queries for elements.

Question

What are fake timers?

Answer

Mocks for setTimeout/setInterval that let you control time in tests.

Question

How do you test network errors?

Answer

Configure MSW to return error responses and verify error handling.

Question

What is the findBy query?

Answer

An async query that waits for an element to appear in the DOM.

Question

What is Testing Async Code?

Answer

Testing Async Code is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Use async/await for clean async tests
  • 2.waitFor retries assertions until timeout
  • 3.Fake timers control setTimeout/setInterval
  • 4.MSW provides realistic API mocking
  • 5.findBy queries wait for elements automatically

Interview Tips

  • Explain how to test async code
  • Discuss fake timers and when to use them
  • Know how to test network requests and errors

Cheat Sheet

Testing Async Cheat Sheet

Async/Await

it('should work', async () => {
  const result = await asyncFunction();
  expect(result).toBe(expected);
});

waitFor

await waitFor(() => {
  expect(screen.getByText('Done')).toBeInTheDocument();
});

Fake Timers

jest.useFakeTimers();
jest.advanceTimersByTime(1000);

MSW

server.use(
  http.get('/api', () => HttpResponse.json(data))
);