Skip to main content

Choosing test layers — Vitest, pytest, Cypress, and Playwright

Separate unit, integration, browser, and production smoke checks, then assign Vitest, pytest, Cypress, and Playwright without duplicating responsibilities.

5 viewsAbout 4 min read
Table of contents

Classify tests by which boundaries they actually cross, not by tool name. Launching a browser does not automatically make a test end to end, and extensive mocking does not automatically make it a unit test. Start with the fact you need to learn when the test fails, then choose the narrowest layer.

Widening the verification boundary

Pure logic

Check input and output quickly.

Component or service

Check framework boundaries and collaborators.

Integration

Check contracts against a real database, cache, or broker.

Browser

Check a user's critical action and visible state.

Production smoke

Check deployed read paths and readiness only.

Choose tools by responsibility

Tool Strong area Typical checks Common misuse
Vitest Fast TypeScript and React unit/component tests Pure functions, hooks, render states, mocked boundaries Treating jsdom as proof of real browser behavior
pytest Python unit, API, and scheduler tests Parsers, FastAPI contracts, time zones, retries, DB adapters Treating mock call counts as proof of DB constraints
Cypress Browser-resident user flows with network control Forms, routing, failed responses, retry UI Rechecking every internal branch through a browser
Playwright Multi-browser, multi-context, multi-tab, and API E2E Authentication, popups, multiple users, route smoke Treating status-only page checks as proof of business success

Cypress and Playwright are not merely rival names. Choose them for the interaction you need. If one product uses both, fix their ownership explicitly—for example, Cypress for screen-focused regression and Playwright for multi-context flows and deployment smoke.

Do not repeat the same defect at every layer

A login button's disabled condition belongs in a component test. Whether a real cookie reaches the next request belongs in browser E2E. Session-store expiry belongs in an integration test.

Duplicate and complementary tests

Duplicate

Recreate the same conditional in both Vitest and Cypress.

Complementary

Vitest checks state transitions; Cypress checks user action and network failure.

Production

Check health, readiness, and public reads instead of replaying write scenarios.

A failure should narrow the cause to one layer. If every test starts a browser and a real database, the suite becomes slow and the broken contract becomes harder to identify.

Mocks versus real dependencies

Mocks are useful for checking how your code calls a dependency. Properties guaranteed by an external system—UNIQUE constraints, transaction isolation, SQL syntax, or Redis TTL—need a real instance.

Mocks are enough
  parser input → output
  dependency failure → visible error state
  retry count and backoff cap

A real dependency is required
  concurrent INSERT deduplication
  transaction rollback
  index-backed query plan
  cache expiry and source reload

Even with Testcontainers or isolated Compose, never use a production database as the test target. Finish destructive and write-path verification in DEV or an ephemeral instance; run only non-destructive smoke checks in production.

Limit browser-test cost

Browser tests cost execution time, image downloads, CI traffic, and investigation effort. Keep the boundary deliberate.

  1. Run critical user paths and changed areas on each pull request.
  2. Move the full browser matrix to scheduled or release gates.
  3. Do not install E2E browser binaries in production application images.
  4. Capture screenshots, traces, and network logs on failure, while removing personal data and tokens.
  5. Keep production smoke checks GET/HEAD-oriented and block mutations by default.

Failure states are part of the contract

A green happy path can still leave users with a blank screen during an outage. Verify at least these states:

  • duplicate submission is blocked while loading
  • 4xx responses explain how to correct input
  • 5xx and network failures offer a retry path
  • empty results differ from failed retrieval
  • partial success is not presented as total success
  • retries do not duplicate the same write

Completion criteria

Test count is not a quality metric. You should be able to explain which evidence protects each changed contract and risk.

  • The narrowest test reproduces the defect and passes after the fix.
  • Integration tests prove guarantees owned by real dependencies.
  • Browser tests protect critical user paths.
  • Build, lint, and type checks pass.
  • Health, readiness, and public smoke pass after deployment.
  • Production writes and paid external calls remain disabled by default.

Next

  • testcontainers
  • vitest-philosophy
  • e2e-equivalence-manifest

See the official documentation for Vitest, pytest, Cypress, and Playwright.

More in quality

All in this category →

Related posts

Was this article helpful?