React Testing


React Testing Interview with follow-up questions

1. What is the importance of testing in React applications?

Testing matters in React apps because it verifies the UI behaves correctly as it evolves, and it catches regressions before users do. Benefits:

  • Confidence to change – refactor or add features without fear of silently breaking existing behaviour.
  • Fewer bugs in production – logic, edge cases, and integration points are exercised automatically.
  • Living documentation – well-written tests describe how a component is supposed to behave.
  • Better design – components that are easy to test tend to be well-structured (clear inputs/outputs).
  • Faster feedback – automated tests run in seconds in CI on every commit.

Typical React testing layers:

  • Unit/component tests – with Vitest or Jest + React Testing Library (RTL), testing components the way a user interacts with them.
  • Integration tests – multiple components/state working together.
  • End-to-end testsPlaywright or Cypress driving a real browser.

The modern philosophy (RTL): test behaviour and accessible output, not implementation details, so tests stay meaningful through refactors.

↑ Back to top

Follow-up 1

Can you explain the concept of unit testing in React?

Unit testing in React involves testing individual components or functions in isolation to ensure they work correctly. It focuses on testing small units of code to verify their behavior and functionality. Unit tests help catch errors early, facilitate code refactoring, and provide documentation for the code.

Follow-up 2

What tools can be used for testing React applications?

There are several tools that can be used for testing React applications. Some popular ones include:

  • Jest: A JavaScript testing framework that comes with built-in support for React.
  • React Testing Library: A library that provides utilities for testing React components in a user-centric way.
  • Enzyme: A JavaScript testing utility for React that makes it easier to test React components' output and behavior.
  • Cypress: A JavaScript end-to-end testing framework that can be used to test React applications in a browser environment.

Follow-up 3

How does testing increase the reliability of a React application?

Testing increases the reliability of a React application by identifying and fixing bugs early in the development process. It helps ensure that the application behaves as expected and meets the requirements. By writing tests, developers can have confidence in the stability and correctness of their code, making it easier to maintain and enhance the application over time.

2. What is Jest and how is it used in React testing?

Jest is a popular JavaScript testing framework (originally from Meta) widely used for React. It bundles a test runner, assertion library (expect), mocking, code coverage, and snapshot testing in one tool with little configuration.

In React testing, Jest provides the runner and assertions, while React Testing Library (RTL) provides utilities to render components and query the DOM. They're used together:

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

test("increments on click", async () => {
  render();
  await userEvent.click(screen.getByRole("button", { name: /increment/i }));
  expect(screen.getByText("Count: 1")).toBeInTheDocument();
});

What Jest gives you: describe/test/it, expect matchers, jest.fn()/jest.mock() for mocks, fake timers, and --coverage.

2026 note: Vitest has become a very common alternative — it's Jest-compatible (same expect/API), but faster and integrates natively with Vite-based projects. Many new React apps use Vitest + RTL; existing apps still widely use Jest + RTL. Either way the testing approach is the same.

↑ Back to top

Follow-up 1

What are some advantages of using Jest for testing?

Some advantages of using Jest for testing are:

  • Easy setup: Jest provides a zero-configuration setup, which means you can start writing tests without any additional configuration.

  • Built-in mocking: Jest comes with built-in mocking capabilities, allowing you to easily mock functions, modules, and dependencies.

  • Snapshot testing: Jest supports snapshot testing, which allows you to capture the output of a component and compare it against a previously saved snapshot. This makes it easy to detect unexpected changes in the component's output.

  • Code coverage: Jest provides built-in code coverage reports, allowing you to see how much of your code is covered by tests.

  • Parallel test execution: Jest can run tests in parallel, which can significantly reduce the overall test execution time.

  • Great documentation and community support: Jest has extensive documentation and a large community, making it easy to find help and resources when needed.

Follow-up 2

Can you explain how to mock a function using Jest?

To mock a function using Jest, you can use the jest.fn() or jest.mock() methods.

  • Using jest.fn(): You can create a mock function using jest.fn(), which creates a new, empty mock function. You can then define the behavior of the mock function by chaining methods like mockReturnValue(), mockResolvedValue(), or mockImplementation().

Here's an example of mocking a function using jest.fn():

// Original function
function add(a, b) {
  return a + b;
}

// Mocking the function
const mockAdd = jest.fn();
mockAdd.mockReturnValue(10);

// Using the mock function
console.log(mockAdd(2, 3)); // Output: 10
  • Using jest.mock(): You can also use jest.mock() to automatically mock a module or dependency. This is useful when you want to mock an entire module or when you want to mock a dependency of the module you are testing.

Here's an example of mocking a function using jest.mock():

// Original module
import { fetchData } from './api';

// Mocking the module
jest.mock('./api', () => ({
  fetchData: jest.fn().mockResolvedValue({ data: 'mocked data' })
}));

// Using the mocked module
fetchData().then(response => {
  console.log(response.data); // Output: 'mocked data'
});

These are just a few examples of how you can mock functions using Jest. Jest provides many more advanced mocking features, such as mocking modules with specific implementations, mocking module dependencies, and mocking timers.

Follow-up 3

How does Jest compare to other testing frameworks you have used?

As an AI, I don't have personal experiences, but I can provide some general comparisons between Jest and other testing frameworks.

  • Jest vs. Mocha: Jest and Mocha are both popular JavaScript testing frameworks, but they have some differences. Jest is an all-in-one testing framework that comes with built-in mocking, code coverage, and snapshot testing. Mocha, on the other hand, is a more flexible framework that allows you to choose your own assertion library, mocking library, and other testing tools. Jest is often considered easier to set up and use, while Mocha provides more flexibility and customization options.

  • Jest vs. Jasmine: Jest and Jasmine are both testing frameworks developed by the same team at Facebook. Jest is built on top of Jasmine and provides additional features like snapshot testing and code coverage. Jest also has better performance compared to Jasmine, as it runs tests in parallel by default.

  • Jest vs. Enzyme: Jest and Enzyme are often used together for testing React applications. Jest provides the testing framework and comes with built-in support for React, while Enzyme is a testing utility library specifically designed for testing React components. Enzyme provides additional features like shallow rendering and DOM manipulation, which can be useful for testing complex React components.

Overall, the choice between Jest and other testing frameworks depends on your specific needs and preferences. Jest is a popular choice for testing React applications due to its simplicity, built-in features, and good performance.

3. Can you explain the concept of snapshot testing in React?

Snapshot testing captures a serialized representation of a component's rendered output and saves it to a .snap file. On later runs, the new output is compared to the stored snapshot; if they differ, the test fails — flagging an unexpected UI change.

import { render } from "@testing-library/react";

test("renders Card", () => {
  const { asFragment } = render();
  expect(asFragment()).toMatchSnapshot();
});

If a change is intentional, you update snapshots with jest --updateSnapshot (or Vitest's -u).

Pros: quick way to detect accidental markup changes; little code to write.

Cons / cautions (why many teams use them sparingly):

  • Brittle – large snapshots fail on trivial changes, training developers to blindly hit "update," which defeats the purpose.
  • Low intent – they assert "output didn't change," not "output is correct."

Best practice: keep snapshots small and focused (a single component/element), and prefer explicit behaviour assertions (RTL queries on roles/text) for most tests, using snapshots only where they add value.

↑ Back to top

Follow-up 1

What are the advantages of snapshot testing?

Snapshot testing offers several advantages:

  1. Easy to create and maintain: Snapshot tests are easy to create as they automatically generate the initial snapshot. They are also easy to maintain as they provide a clear visual representation of the expected output.
  2. Quick feedback: Snapshot tests provide quick feedback by comparing the rendered output with the saved snapshot. If the UI changes unexpectedly, the test fails, allowing developers to identify and fix issues early.
  3. Regression testing: Snapshot tests act as a form of regression testing by ensuring that previously working components continue to render correctly after code changes.
  4. Documentation: Snapshots serve as documentation for the expected output of a component, making it easier for developers to understand the intended UI behavior.

Follow-up 2

In what scenarios would you use snapshot testing?

Snapshot testing is useful in the following scenarios:

  1. UI components: Snapshot testing is commonly used to test UI components in React applications. It helps ensure that the components render correctly and consistently.
  2. Styling changes: Snapshot testing can be used to verify that styling changes, such as CSS modifications or theme updates, do not affect the visual appearance of components.
  3. Refactoring: When refactoring code, snapshot testing can be used to ensure that the behavior and appearance of components remain unchanged.
  4. Integration testing: Snapshot testing can also be used for integration testing, where the rendered output of multiple components or a tree of components is compared against a snapshot.

Follow-up 3

How do you update a snapshot in Jest?

To update a snapshot in Jest, you can use the --updateSnapshot flag when running your tests. For example, you can run jest --updateSnapshot to update all the snapshots in your project. Alternatively, you can use the u shortcut key when running Jest in watch mode (jest --watch). This will prompt you to update the snapshots one by one.

It is important to note that updating snapshots should be done with caution. Snapshots should only be updated when intentional changes have been made to the component or its output. Updating snapshots without proper review can lead to false positives or hide actual issues in the code.

4. What is Enzyme in React testing and how is it used?

Enzyme was a popular React testing utility from Airbnb for rendering components and traversing/manipulating their output (shallow, mount, find, simulate). It emphasized inspecting a component's internal implementation (state, instance methods, child components).

Important 2026 context: Enzyme is effectively dead and should not be used in new projects. It's unmaintained, and its official adapters never supported React 17+ (React 18/19 are unsupported via community hacks at best). The ecosystem has moved on.

The standard today is React Testing Library (RTL), used with Vitest or Jest. RTL's philosophy is the opposite of Enzyme's: test components the way a user experiences them — query by accessible role/label/text and assert on visible behaviour — rather than poking at internal state. This produces tests that survive refactors.

// RTL — behaviour-focused, the modern approach
render();
await userEvent.type(screen.getByLabelText(/email/i), "[email protected]");
await userEvent.click(screen.getByRole("button", { name: /submit/i }));
expect(screen.getByText(/welcome/i)).toBeInTheDocument();

So: know what Enzyme was (you'll see it in legacy code), but reach for React Testing Library for anything new.

↑ Back to top

Follow-up 1

What are the differences between shallow rendering and full rendering in Enzyme?

In Enzyme, shallow rendering and full rendering are two different approaches to rendering React components.

  • Shallow rendering: Shallow rendering renders only the component being tested, without rendering its child components. It is useful for isolating the component under test and focusing on its behavior. Shallow rendering is faster than full rendering and does not require a DOM.

  • Full rendering: Full rendering renders the component and all of its child components, creating a complete DOM tree. It is useful for testing the interaction between components and their child components. Full rendering is slower than shallow rendering and requires a DOM environment.

Follow-up 2

How can you simulate user interaction using Enzyme?

Enzyme provides several methods to simulate user interaction with React components:

  • simulate(event[, mock]): Simulates an event on the selected element(s). The event parameter can be a string representing the event type (e.g., 'click', 'change') or a synthetic event object. The mock parameter is an optional object that can be used to provide additional event properties.

  • prop(key[, value]): Sets or gets the value of a prop on the selected element(s). This can be used to simulate changes in props that trigger different behaviors.

  • setState(newState[, callback]): Sets the state of the selected component(s) to the specified newState. The callback parameter is an optional function that will be called after the state is updated.

These methods can be used in combination to simulate various user interactions and test the resulting behavior of the React components.

Follow-up 3

What are some limitations of Enzyme?

While Enzyme is a powerful testing utility, it has some limitations:

  • Enzyme does not support React Hooks. As of now, Enzyme does not have built-in support for testing components that use Hooks. However, there are workarounds available, such as using the @testing-library/react-hooks package.

  • Enzyme does not fully support React's concurrent mode. Enzyme's shallow rendering and full rendering methods may not work correctly with components that use concurrent mode. It is recommended to use React Testing Library or other testing utilities for testing components in concurrent mode.

  • Enzyme does not provide built-in support for testing React Native components. Enzyme is primarily designed for testing React components in a browser environment and may not work correctly with React Native components. For testing React Native components, it is recommended to use libraries specifically designed for React Native testing, such as react-native-testing-library.

5. How do you test React components that have state?

With React Testing Library, you don't test state directly — you drive the component like a user and assert on the visible result, which implicitly verifies the state logic.

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

test("counter increments", async () => {
  render();
  // 1. assert initial rendered state
  expect(screen.getByText("Count: 0")).toBeInTheDocument();

  // 2. trigger a state change via a user interaction
  await userEvent.click(screen.getByRole("button", { name: /increment/i }));

  // 3. assert the updated state is reflected in the UI
  expect(screen.getByText("Count: 1")).toBeInTheDocument();
});

Steps: render the component, query its initial output, simulate user actions (userEvent) that change state, then assert the updated output. Use findBy* queries (which await) for state changes that happen after async work.

Key principle: assert on what the user sees, not on internal state variables. This keeps tests robust to refactors (e.g. switching useState to useReducer) — the opposite of the old Enzyme approach of inspecting instance().state. Wrapping in act() is handled for you by RTL's render/userEvent.

↑ Back to top

Follow-up 1

How do you test the initial state of a component?

To test the initial state of a component, you can use the getByTestId or queryByTestId function from React Testing Library to access the component's state. Then, you can use the expect function from Jest to assert the initial state. Here's an example:

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

it('should have initial count of 0', () => {
  const { getByTestId } = render();
  const countElement = getByTestId('count');
  expect(countElement.textContent).toBe('0');
});

In this example, we render a Counter component and access the count state using getByTestId('count'). We then assert that the textContent of the countElement is '0', which represents the initial state of the component.

Follow-up 2

How do you test state changes in response to user actions?

To test state changes in response to user actions, you can use the fireEvent function from React Testing Library to simulate user actions that trigger state changes. Then, you can use the expect function from Jest to assert the updated state. Here's an example:

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

it('should increment count on button click', () => {
  const { getByTestId } = render();
  const countElement = getByTestId('count');
  const incrementButton = getByTestId('increment-button');

  fireEvent.click(incrementButton);

  expect(countElement.textContent).toBe('1');
});

In this example, we render a Counter component and access the count state using getByTestId('count'). We then simulate a button click using fireEvent.click(incrementButton) and assert that the textContent of the countElement is '1', which represents the updated state of the component after the button click.

Follow-up 3

What challenges have you faced when testing stateful components and how did you overcome them?

When testing stateful components, some common challenges include:

  1. Asynchronous state updates: If the component's state updates asynchronously, it can be challenging to test the updated state. To overcome this, you can use the waitFor function from React Testing Library to wait for the state to update before asserting the updated state.

  2. Mocking dependencies: If the component relies on external dependencies, such as API calls, it can be challenging to mock these dependencies for testing. You can use mocking libraries like jest.mock or dependency injection techniques to mock these dependencies.

  3. Testing complex state transitions: If the component's state transitions are complex, it can be challenging to cover all possible state transitions in your tests. In such cases, you can prioritize testing the critical state transitions and use code coverage tools to identify any untested state transitions.

By being aware of these challenges and using appropriate testing techniques, you can effectively test stateful components in React.

Live mock interview

Mock interview: React Testing

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.