AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 15 — Testing — Unit, Integration, and End-to-End in Node.js

15 — Testing — Unit, Integration, and End-to-End in Node.js

August 13, 20266 min read
Download as Markdown

"Write a test, run the test" was my testing model, and it skipped the decision that matters most: what kind of test am I writing. The model that finally stuck is a spectrum: tests range from unit (one function, fully isolated) to integration (several units together) to end-to-end (a real browser driving the whole app), and the tools split cleanly along that line. Vitest, Jest, and node:test cover the left side; Cypress and Playwright cover the right [1]. Knowing which kind of test I am writing is most of the decision.

The framing that landed for me is the spectrum, then the tools that map to each region.

The testing spectrum

Unit Integration End-to-End fast, isolated, many slow, whole-app, few confidence in the whole system rises → Vitest · Jest · node:test Cypress · Playwright

The axis is confidence versus cost. A unit test is fast and pinpoints failures but proves only that one function works in isolation. An E2E test is slow and harder to debug but proves the whole system works together as a user experiences it. Real applications need both, weighted toward the unit end — the classic "testing pyramid" recommends many unit tests, fewer integration tests, and a small number of E2E tests [1].

Unit and integration: Vitest, Jest, node:test

Three tools cover the left and middle of the spectrum, and they are more similar than different. Each provides a way to write a test, run it, and report pass/fail.

Jest was the long-standing default — a delightful, everything-included framework with describe/it/expect, mocks, and snapshot testing [2]. It works with Babel, TypeScript, React, Vue, and more, and an enormous amount of existing code uses it.

Vitest is the modern, Vite-native successor [3]. It is Jest-compatible (the same describe/it/expect API, so tests port with minimal changes), but it runs on Vite, which means native ESM and TypeScript support out of the box, faster startup, and hot-module-aware watching. For new projects — especially Vite-based ones — Vitest is now the default I reach for.

import { describe, it, expect } from 'vitest';
import { add } from './math';

describe('add', () => {
it('sums two numbers', () => {
expect(add(2, 3)).toBe(5);
});
});

node:test is Node's built-in test runner, added in Node 18 [4]. It has no dependencies, ships with Node, and provides the same describe/it/expect-style API. For a project that wants zero test-tooling dependencies — a library, a CLI, anything that values a minimal install — node:test is the practical choice.

import { test } from 'node:test';
import assert from 'node:assert/strict';

test('add sums two numbers', () => {
assert.equal(add(2, 3), 5);
});

The three are interchangeable enough that the choice is mostly about ecosystem fit: Jest for existing codebases, Vitest for new Vite-based projects, node:test for zero-dependency minimalism. The test shape — small, fast, isolated — is the same in all three.

End-to-end: Cypress and Playwright

The right end of the spectrum needs a different kind of tool, because the test has to drive a real browser the way a user would — click buttons, fill forms, read the rendered DOM, and assert on it. Cypress and Playwright are the two dominant options [5][6].

Cypress runs tests directly in the browser and ships a visual test runner that shows each command and the DOM state at every step, which makes debugging E2E tests genuinely pleasant. It is primarily oriented toward testing web applications in the browser.

Playwright, from Microsoft, supports multiple browsers (Chromium, Firefox, WebKit) and lets me write tests that simulate real user behavior across them [6]. It is faster for large suites and handles modern app patterns (network interception, multi-tab, file downloads) cleanly. Playwright can also be used for scraping and automating browser workflows, not only testing.

import { test, expect } from '@playwright/test';

test('user can log in', async ({ page }) => {
await page.goto('/login');
await page.fill('[name=email]', 'ave@example.com');
await page.fill('[name=password]', 'secret');
await page.click('button[type=submit]');
await expect(page).toHaveURL('/dashboard');
});

Both tools serve the same purpose — prove the whole system works as a user experiences it. Playwright's cross-browser coverage and speed make it the more common pick for new projects; Cypress's dev experience and runner make it a strong choice for teams that value the visual debug loop.

The shape of a test, regardless of tool

Underneath the tool choice, every test has the same three-part shape — arrange, act, assert [1]:

  1. Arrange — set up the preconditions (inputs, mocks, a fresh database state).
  2. Act — call the thing under test.
  3. Assert — verify the result matches the expectation.

Unit tests make "arrange" trivial (a few variables) and "assert" precise (one return value). E2E tests make "arrange" heavy (a running app, a seeded database, a browser session) and "assert" coarse (the page shows the right thing). Recognizing that the core shape is identical — and only the weight of each part changes — is what keeps the spectrum legible.

How I use this

The model I keep is the spectrum, and it drives where I invest test effort. I write many unit tests with Vitest (or node:test for library work) — they are fast, they pinpoint failures, and they document how each function behaves. I write a smaller number of integration tests for the seams that unit tests miss (a function plus its real database, an API handler end to end). I write a small, curated set of E2E tests with Playwright covering the few critical user flows — login, checkout, the one path that must never break. The discipline is keeping the pyramid weighted toward the fast, isolated end: a suite of a thousand unit tests that runs in two seconds is worth more than a hundred E2E tests that take twenty minutes. The spectrum framing keeps the investment deliberate.

References

[1] "Software testing," Wikipedia. [Online]. Available: https://en.wikipedia.org/wiki/Software_testing

[2] "Jest," jestjs.io. [Online]. Available: https://jestjs.io

[3] "Vitest," vitest.dev. [Online]. Available: https://vitest.dev/

[4] OpenJS Foundation, "Test runner," Node.js API Docs. [Online]. Available: https://nodejs.org/api/test.html

[5] "Cypress," cypress.io. [Online]. Available: https://www.cypress.io/

[6] "Playwright," playwright.dev. [Online]. Available: https://playwright.dev/

Knowledge check · Question 1 of 5

On the testing spectrum, a unit test is characterized by…

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!