Your async tests pass 9 times out of 10 and fail in CI for no reason. The real bug is in how you test time-dependent code. Here is how to replace setTimeout with deterministic clocks, control promise ordering, and eliminate flakiness from every async test in your suite.
Most Node.js codebases either hardwire every dependency or import a heavyweight DI framework and regret it. This post shows three practical DI patterns with working code: a manual wiring function, a lightweight factory-based container, and function-scoped injection. Pick the pattern that fits your project size.
Node.js has shipped a production-ready test runner since v20 that needs zero config, runs TypeScript natively, and handles 90% of what you reach for Jest or Mocha to do. Here is the migration path, the API patterns that matter, and the edge cases where you still need a third-party tool.
Your seed.sql is three months out of date, your integration tests pass on your machine but fail on CI because the assumed data does not exist, and nobody knows how to add a new entity to the test dataset without breaking six other tests. Here is the factory-based seeding pattern that makes test data declarative, reproducible, and easy to maintain.
Your service works in isolation but breaks when paired with a different version of its upstream. That is not a bug in either service. It is a contract nobody wrote down. Here is how to catch every breaking API change before deploy using consumer-driven contract tests with Pact, with working Node.js examples.
You wrote 60 unit tests and deployed confident. The bug shipped anyway because nobody thought to try an empty array with a negative offset. Property-based testing generates hundreds of edge cases automatically and surfaces the scenario you forgot to imagine. Here is how to use Fast-Check in real Node.js projects to catch the bugs example-based tests miss.
A migration that looks fine in a code review drops a production table on deploy. Here is a CI pipeline that catches destructive changes, tests rollbacks against real data, and verifies performance impact before your database ever sees a DDL statement.
The billing module has 4,000 lines of untested JavaScript, nobody on the team wrote it, and every deploy triggers the "please do not break billing" prayer. Here is the three-phase strategy (characterization tests, strangler fig extraction, and feature flag cutover) that rewrites legacy code incrementally without a freeze or a big bang.
Unit tests pass, CI is green, and your API still crashes in production because a query locks a row you never lock in tests. Here is how to run Node.js integration tests against real Postgres, Redis, and Kafka with Docker, cut the mock tax, and catch the race conditions that only exist when real connections are involved.
Cypress invented the modern E2E testing experience. Playwright surpassed it on capability, speed, and parallelism. Here is the side-by-side that matters: real-world test runtime, which APIs are stable, the cross-browser story, and the migration cost if you're already on Cypress.