React · Testing

Testing Interview Questions
With Answers & Code Examples

3 carefully curated Testing interview questions with working code examples and real interview gotchas.

Practice Interactively →← All Categories
3 questions0 beginner2 core1 advanced
Q1Core

What is React Testing Library and what is its testing philosophy?

💡 Hint: Test behavior from the user's perspective — query by role/label/text, not by class or test ID

React Testing Library (RTL) is built around one guiding principle: "The more your tests resemble the way your software is used, the more confidence they can give you."

Query priority (use in this order):

  1. getByRole — best, mirrors what screen readers see
  2. getByLabelText — form inputs by their label
  3. getByText — visible text content
  4. getByTestId — last resort only
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('increments counter when button is clicked', async () => {
  const user = userEvent.setup();
  render();

  // Query by role — accessible, matches what users see
  const button = screen.getByRole('button', { name: /increment/i });
  const count  = screen.getByText('0');

  await user.click(button);

  expect(screen.getByText('1')).toBeInTheDocument();
});

test('shows error for invalid email', async () => {
  const user = userEvent.setup();
  render();

  await user.type(screen.getByLabelText(/email/i), 'not-an-email');
  await user.click(screen.getByRole('button', { name: /submit/i }));

  expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
});
💡 Avoid testing implementation details (state values, method calls). If you refactor the internals but the behavior is the same, tests should still pass. RTL forces you toward this naturally.
Practice this question →
Q2Core

How do you test custom hooks?

💡 Hint: renderHook from @testing-library/react — wraps the hook in a minimal component
import { renderHook, act } from '@testing-library/react';

// Hook under test
function useCounter(initial = 0) {
  const [count, setCount] = useState(initial);
  const increment = useCallback(() => setCount(c => c + 1), []);
  const reset = useCallback(() => setCount(initial), [initial]);
  return { count, increment, reset };
}

// Test
test('useCounter increments and resets', () => {
  const { result } = renderHook(() => useCounter(5));

  expect(result.current.count).toBe(5);

  act(() => { result.current.increment(); });
  expect(result.current.count).toBe(6);

  act(() => { result.current.reset(); });
  expect(result.current.count).toBe(5);
});

// Hook with context dependency — provide the context wrapper
test('useAuth returns user from context', () => {
  const wrapper = ({ children }) => (
    {children}
  );

  const { result } = renderHook(() => useAuth(), { wrapper });
  expect(result.current.user).toEqual(mockUser);
});
💡 Always wrap state updates in act(). RTL's userEvent does this automatically, but direct calls to hook functions need act() to flush React's update queue.
Practice this question →
Q3Advanced

How do you mock API calls in React tests?

💡 Hint: MSW (Mock Service Worker) intercepts network requests at the service worker level — no mocking of fetch/axios
// ─── Using MSW (recommended) ──────────────────────────────────────────────────
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';

const server = setupServer(
  http.get('/api/users', () => {
    return HttpResponse.json([{ id: 1, name: 'Alice' }]);
  }),
  http.post('/api/login', async ({ request }) => {
    const body = await request.json();
    if (body.password === 'wrong') {
      return HttpResponse.json({ message: 'Invalid password' }, { status: 401 });
    }
    return HttpResponse.json({ token: 'abc123' });
  })
);

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

test('displays users from API', async () => {
  render();
  expect(screen.getByText(/loading/i)).toBeInTheDocument();
  await screen.findByText('Alice'); // waits for async render
});

// Override for a specific test
test('shows error on failed login', async () => {
  server.use(
    http.post('/api/login', () => {
      return HttpResponse.json({ message: 'Server error' }, { status: 500 });
    })
  );
  render();
  // ... assert error state
});
💡 MSW is the gold standard because it intercepts at the network level — your code uses the real fetch, so you test the actual data-fetching logic, not a mock of fetch. Works in both tests and browser for development.
Practice this question →

Other React Interview Topics

Rendering StrategiesCore JSType SystemReact FundamentalsFunctionsMicrofrontendsGenericsAsync JSHooksObjectsMonorepoArrays'this' KeywordUtility TypesError HandlingModern JSBundle OptimizationPerformanceDOM & EventsState ManagementClasses & OOPCaching StrategiesComponent PatternsAdvanced TypesAuthenticationReact RouterFormsAdvanced PatternsFrontend SecurityConcurrent ReactServer ComponentsEcosystemNetwork OptimizationCore Web VitalsBrowser APIs

Ready to practice Testing?

Get AI feedback on your answers, predict code output, and fix real bugs.

Start Free Practice →