nock 14.0.17 vs msw 2.15.0 on 2,000 sequential mocked fetch calls in Node 20: 2.85 ms vs 0.49 ms per request (~5.8x); via axios 3.80 ms vs 1.41 ms (~2.7x). nock sets up faster (97 vs 208 ms), crossover near 50 requests per process.
5 min read
I mocked the same 2,000 sequential fetch calls in Node 20 with nock 14.0.17 and msw 2.15.0: nock took a median 2.85 ms per request, msw took 0.49 ms (about 5.8x faster). Through axios, which goes through Node's http module instead of fetch, the gap shrank to 3.80 ms versus 1.41 ms per request (about 2.7x).
Short answer: switch to msw if your Node tests mock hundreds of fetch calls, or if you want the same handlers in the browser. Stay on nock if your suite is small, leans on nock-only features like scope.done() and recorder mode, and runs fast enough today. No affiliate links in this post.
/workspace/bench-msw-nock/run.mjs. One mocked endpoint GET https://api.example.test/users/:id returning a small JSON object. After one check request, the script awaits 2,000 requests in a row and checks each response id. All 2,000 came back correct in every run.setupServer, onUnhandledRequest: 'error'), nock 14.0.17 (persist(), regex path, disableNetConnect()), axios 1.20.0. Baseline: a real node:http server on 127.0.0.1 with no mocking library.performance.now() around the 2,000-request loop, plus a separate number for import + setup + first request, plus the full process wall clock.| Variant | Median per request | Loop for 2,000 | Setup + first request |
|---|---|---|---|
| msw 2.15 + fetch | 0.49 ms | 987 ms | 208 ms |
| nock 14 + fetch | 2.85 ms | 5,710 ms | 97 ms |
| msw 2.15 + axios | 1.41 ms | 2,813 ms | 306 ms |
| nock 14 + axios | 3.80 ms | 7,594 ms | 187 ms |
| real loopback server + fetch | 0.48 ms | 955 ms | 80 ms |
| real loopback server + axios | 0.52 ms | 1,031 ms | 165 ms |
On this fixture, yes, per request. msw on fetch was about as fast as hitting a real server on loopback. nock loads faster, so a process that makes only ten requests finishes first under nock: about 126 ms (97 + 29) versus 213 ms (208 + 5). The crossover in my numbers is around 50 requests per process. A suite that mocks 500 fetch calls would spend about 1.4 s in nock versus 0.25 s in msw.
If you are also choosing the test runner, see the Jest vs Vitest post and the jsdom vs happy-dom for Vitest post.
Axios in Node uses the http module, not fetch. Both libraries intercept that path by patching http.ClientRequest, and that layer cost more than fetch interception in my runs: msw went from 0.49 to 1.41 ms per request, nock from 2.85 to 3.80 ms. Without mocking, axios over loopback was 0.52 ms, so most of that extra time came from interception, not from axios. If your code calls an http-based client, expect a smaller gap between the two libraries than the fetch numbers suggest.
| Situation | My pick |
|---|---|
| Node tests with hundreds of mocked fetch calls | msw |
| Same request mocks wanted in browser dev and in tests | msw |
| Small suite, under about 50 mocked requests per test file | Stay on nock; the gap is noise |
You rely on scope.done() assertions or nock's recorder | Stay on nock, or plan to rebuild those checks |
| Already on Node 22.12+ and starting fresh | msw 3.x is an option, but I did not test it here |
Bottom line: 2,000 fetch calls took 0.99 s with msw and 5.71 s with nock on the same box. Who should not switch yet: teams with a large nock suite built on persist() chains and isDone() checks, where rewriting every mock costs more than the seconds you would save. For related test-speed choices, see the Vitest 5 upgrade post and the c8 vs nyc coverage post; for running TypeScript tests directly, the tsx vs ts-node post.
How this was made: I wrote the fixture, installed both libraries on the ShopperCove test box, timed interleaved runs in fresh processes and kept the JSON; the write-up was drafted with AI help and checked against that output.
Lab evidence
Hands-on on ShopperCove box 5 Oct 2026 ~23:38-23:43 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 20.19.2). Fixture: run.mjs, GET Open api.example.test JSON, 1 check request then 2000 sequential awaited requests, ids verified (2000/2000 every run). msw 2.15.0 setupServer onUnhandledRequest error; nock 14.0.17 persist + regex + disableNetConnect; axios 1.20.0; baseline node:http server on 127.0.0.1. Fresh process per run, 1 warmup, 7 interleaved rounds. Loop medians: msw fetch 987.1 ms (0.49 ms/req), nock fetch 5709.6 ms (2.85 ms/req), real fetch 955.3 ms; msw axios 2813.1 ms (1.41 ms/req), nock axios 7594.1 ms (3.80 ms/req), real axios 1031.1 ms. Setup+first request: msw fetch 207.8 ms, nock fetch 96.5 ms, msw axios 306.4, nock axios 187.3. nock static reply: 5671/5726/5689 ms (no change). Both use @mswjs/interceptors 0.41. msw 3.0.2 needs Node >=22.12 (not run). Raw: /workspace/bench-msw-nock/res-msw-nock.json. Not tested: browser/service worker, Vitest/Jest wiring, concurrency, GraphQL, recorder, Windows/macOS. No affiliate.
Hands-on6 Oct 20268 min read
Hands-on6 Oct 20264 min read
Playwright 1.55.1 Chromium vs Cypress 14.5.4 Electron on one local smoke (visit /, assert h1, click, assert). Warm medians (7 rounds after 1 warm-up): Playwright 1,466 ms vs Cypress 4,243 ms (~2.9x). Disk: PW node_modules ~12 MB + cache ~918 MB; Cypress node_modules ~23 MB + cache ~601 MB. Fixture /workspace/bench-pw-cy/.
Hands-on6 Oct 20264 min read