ProCust
Testing React Applications: Unit, Integration and E2E Tests

Testing React Applications: Unit, Integration and E2E Tests

How to test React applications sensibly: which tests pay off, which tools fit, and how a test strategy catches bugs early instead of slowing the team down.

In short: A good test strategy for React applications works on three levels: static checks with TypeScript and ESLint, plenty of component-level integration tests with Vitest and React Testing Library, and a small set of targeted end-to-end tests with Playwright for business-critical flows. Tests check behaviour from the user's point of view, not internal implementation. That way they catch real bugs and do not break with every refactor.

Why tests in React projects often fail

Many teams have tests but do not trust them. Either the tests break on every small code change while the app still works, or they pass while something is broken in the browser. Both have the same cause: the tests check implementation details such as internal state or component names instead of what users see and do.

A test strategy therefore answers two questions. What do we want to protect, and at which level is that cheapest?

The levels of a test strategy

Static analysis

The cheapest bugs are the ones that never reach a test. TypeScript catches wrong props, missing null checks and typos in field names before the code runs. ESLint with the React Hooks rules flags common dependency mistakes. Both run in the editor and in the CI pipeline. Our post on clean code principles for TypeScript goes further.

Unit tests for pure logic

Functions without UI, such as price calculations, validation, date logic or formatting, are tested directly with Vitest or Jest. These tests are fast, stable and good for covering many edge cases. Vitest has become the default in projects built with Vite because it shares the same configuration.

Component-level integration tests

This is where most of the value lies. With React Testing Library you render a component or a whole screen, interact with it the way a user would and check the result. Elements are found by role and label, for example "button named Save", rather than by CSS class. A side effect: a UI that is easy to test this way is usually accessible to screen readers too.

API calls are mocked at the network level with Mock Service Worker (MSW). The component uses its real data fetching but gets controlled responses, including errors and slow replies.

End-to-end tests for critical flows

E2E tests drive a real browser through the running application, backend included. Playwright is the most common choice today, with Cypress as an alternative. These tests are slower and need more upkeep, so limit them to flows whose failure really hurts: login, checkout, creating an order, payment. For React Server Components, which render on the server, E2E tests are currently often the most reliable check.

What to test and what to skip

Prioritise by risk. Business rules where a bug costs money, forms with validation, permissions and flows customers use daily deserve thorough tests. Purely presentational components without logic rarely need their own tests, because the screen-level integration tests cover them anyway.

Snapshot tests of whole components are tempting because they are easy, but they tend to get updated without thought and then catch nothing. For visual changes, screenshot comparisons in a real browser are more reliable.

Coverage is a useful pointer to untested areas, not a quality goal. You can reach a high number with tests that check nothing meaningful.

Making tests part of daily work

Tests only help if they run on every change. In the CI pipeline, static checks and component tests run on every pull request, and E2E tests before every deployment. Our practical guide to CI/CD pipelines shows the setup.

Flaky tests that pass one run and fail the next should be fixed or removed immediately. Otherwise the team learns to ignore red pipelines. Adding missing tests to an existing app is one way of paying down technical debt, which we discuss in technical debt in startups.

Frequently asked questions (FAQ)

Are tests worth it for smaller projects?

Yes, in a lighter form. TypeScript, a few integration tests for the most important forms and one E2E test for the main flow already prevent many embarrassing bugs. The effort is small compared with a bug your customers find first.

Jest or Vitest?

Both work well with React Testing Library and have a very similar API. Vitest is the obvious choice for Vite projects and usually faster. In existing Jest projects there is rarely a compelling reason to switch.

How many end-to-end tests do I need?

As few as possible and as many as necessary. A set of tests covering your application's core flows is often enough. Every extra E2E test costs run time and maintenance and should have a clear reason.

Who writes the tests, developers or a separate QA team?

In modern teams, developers write tests alongside the code because they know the logic best. A QA role adds exploratory testing and checks from the user's perspective. Together they give the best coverage.

Conclusion

Tests exist to give you confidence that a change breaks nothing. They do that when they check user behaviour, sit at the cheapest possible level and run automatically on every change. A small, reliable test suite is worth more than a large one nobody trusts.

If you want to build a React application on a solid testing foundation or secure an existing one, talk to us about web development.