Mock de peticiones del lado del servidor en Playwright (Next.js, TanStack Start)
En una app de Next.js o TanStack Start, el servidor llama a tu API mientras renderiza una página. El mocking de peticiones de Playwright nunca ve esas llamadas. Las secciones siguientes explican por qué y luego comparan cuatro formas de controlarlas en una prueba de Playwright: el modo de prueba de Next.js (Next.js test mode), MSW dentro del servidor, un proxy de reenvío (forward proxy) como mockttp y la grabación de respuestas reales con test-proxy-recorder. Los datos sobre las demás herramientas se comprobaron con su propia documentación y su código fuente el 2026-10-03.
Por qué page.route() no ve las peticiones del lado del servidor
Sección titulada «Por qué page.route() no ve las peticiones del lado del servidor»El page.route() de Playwright actúa sobre las «peticiones de red que hace una página». En cambio, un Server Component, un loader de ruta o una server function se ejecutan en el proceso Node.js de tu app. Su fetch va desde ese proceso directamente a la API. La petición nunca pasa por el navegador, así que Playwright no tiene nada que interceptar. page.route() sí ve la petición de la página que el navegador envía a tu app, y el HTML que vuelve. Nunca ve las peticiones que hizo el servidor para construir ese HTML.
Los archivos HAR tienen el mismo límite, porque page.routeFromHAR() sirve «las peticiones de red que se hacen en la página».
La comparación de MSW con Playwright dice lo mismo: page.route() afecta «al tráfico del navegador lanzado, no al proceso de Node.js». La publicación del equipo de Playwright sobre el mocking del lado del servidor dice que page.route() funciona para las peticiones del navegador, y «para las llamadas HTTP del lado del servidor, no». La solicitud de funcionalidad para integrar el mocking del lado del servidor, microsoft/playwright#30766, se cerró el 2026-05-22 con un enlace a esa publicación.
Modo de prueba de Next.js
Sección titulada «Modo de prueba de Next.js»Next.js incluye una integración experimental con Playwright en next/experimental/testmode/playwright. Su única documentación es un README en el repositorio de Next.js, y la guía de Playwright de Next.js no la menciona. Una prueba registra handlers con next.onFetch(), y Next.js les envía las llamadas fetch del servidor:
// Abreviado del READMEimport { test, expect } from 'next/experimental/testmode/playwright';
test('/product/shoe', async ({ page, next }) => { next.onFetch((request) => { if (request.url === 'http://my-db/product/shoe') { return new Response(JSON.stringify({ title: 'A shoe' }), { headers: { 'Content-Type': 'application/json' }, }); } return 'abort'; });
await page.goto('/product/shoe'); await expect(page.locator('body')).toHaveText(/Shoe/);});Lo que necesita:
experimental: { testProxy: true }ennext.config.js.- Un
playwright.config.tscreado condefineConfigdenext/experimental/testmode/playwright, y pruebas que importentestdel mismo módulo.
Por dentro, cada worker de Playwright arranca un pequeño servidor proxy. El fixture añade a las peticiones de la página una cabecera Next-Test-Proxy-Port (ese proxy) y una cabecera Next-Test-Data (el id de la prueba) (next-fixture.ts). Con testProxy activado, el servidor lee esas cabeceras y envía cada fetch del lado del servidor a ese proxy. El proxy entrega la petición a los handlers de esa prueba (fetch.ts). Los handlers se guardan por id de prueba, así que las pruebas en paralelo pueden compartir un mismo servidor de Next.js. Un fetch del servidor al que no responde ningún handler lanza Proxy request aborted. En su lugar, un handler puede devolver 'continue' para dejar pasar la petición. next.onFetch() también recibe las llamadas fetch de la propia página a otros orígenes.
Estado:
- Experimental. Tanto la ruta de importación como la clave de configuración lo indican, y el README se titula «Experimental test mode for Playwright».
- Sigue recibiendo correcciones. La más reciente, vercel/next.js#96525, se fusionó el 2026-08-13.
- El README también ofrece handlers de MSW por prueba a través de
next/experimental/testmode/playwright/msw. Con MSW 3.0 (publicado el 2026-09-28) ese punto de entrada no llega a cargarse. Importastrict-event-emitter, del que MSW 3.0 ya no depende. También llama ahandleRequest()de MSW, que la 3.0 eliminó (notas de la versión 3.0 de MSW,msw.ts). Con MSW 2.15.0 sí se carga. Ambos resultados son de Next.js 16.3.8, comprobados el 2026-10-03.
MSW en el servidor de Next.js
Sección titulada «MSW en el servidor de Next.js»MSW intercepta peticiones en Node.js con setupServer de msw/node (integración con Node.js). Su documentación no tiene una guía para Next.js. Cuando el autor de MSW cerró el issue sobre el App Router, señaló mswjs/examples#101 como la referencia de integración (comentario). Ese pull request está abierto desde el 2024-01-22. Arranca el interceptor desde el root layout, con una lista fija de handlers:
// app/layout.tsx, de mswjs/examples#101if (process.env.NEXT_RUNTIME === 'nodejs') { const { server } = require('@/mocks/node'); // setupServer(...handlers) server.listen();}Los handlers viven entonces en el proceso de Next.js. El ejemplo arranca ese proceso con el webServer de Playwright, que ejecuta un solo proceso para toda la ejecución (publicación del equipo de Playwright). Todos los workers envían sus páginas a ese único servidor, y los workers en paralelo cambian cómo se comportan sus handlers:
- Una prueba no puede llamar a
server.use()en ese servidor, porque la prueba se ejecuta en su propio proceso de worker. MSW no tiene ninguna API publicada para cambiar handlers en otro proceso. La propuesta,setupRemoteServeren mswjs/msw#1617, está abierta desde el 2023-05-12. La integración de MSW para Playwright dice que depende depage.route()hasta que eso se publique (@msw/playwright). - Si añades tu propio canal, como una ruta de API que llame a
server.use(), el cambio llega a todas las pruebas. Los handlers añadidos conserver.use()«persisten en la instancia del servidor». La publicación que presenta server boundary explica que las sobrescrituras concurrentes en un mismo servidor se convierten en «un estado global compartido entre todas las pruebas». En #1617, el autor de MSW señala que, en la práctica, o mantienes «una lista fija de handlers» o arrancas «una instancia de la app por caso de prueba».
En la práctica, MSW dentro de un servidor de Next.js compartido da las mismas respuestas a todas las pruebas en paralelo. Dos herramientas añaden handlers por prueba sobre MSW. El modo de prueba de Next.js acepta handlers de MSW (con MSW 2, ver arriba). scenarist identifica los escenarios por una cabecera x-scenarist-test-id que el código de tu servidor reenvía en cada fetch (adaptador de Next.js).
Un proxy de reenvío como mockttp
Sección titulada «Un proxy de reenvío como mockttp»Cuando el equipo de Playwright cerró #30766, remitió a la publicación de Simon Knott. Esta pone mockttp, un servidor de mocks y proxy HTTP y HTTPS, delante del tráfico saliente del servidor. Cada prueba le añade reglas, como mocks.forPost(url).thenJson(200, body).
El código de la app no cambia. Su proceso de servidor arranca con variables de entorno que envían las peticiones salientes a través del proxy:
HTTP_PROXYyHTTPS_PROXYcon la URL del proxy.NODE_USE_ENV_PROXY=1, para que elfetchintegrado de Node use esas variables. Node.js la añadió en 24.0.0 y 22.21.0, y la marca como en desarrollo activo (documentación de Node.js).NODE_EXTRA_CA_CERTSapuntando al certificado de CA del proxy, para que el servidor confíe en los certificados que mockttp genera para los hosts HTTPS.
El proxy pertenece a un worker de Playwright, así que la publicación arranca un servidor de la app por worker desde un fixture de worker, cada uno en un puerto libre. Evita el webServer de Playwright, que arranca un solo proceso para toda la ejecución antes de que exista ningún worker. En la configuración de la publicación, las peticiones sin regla pasan a la API real. La publicación sugiere responderlas con un error, para que un mock olvidado salga a la luz.
El equipo de Playwright describe el enfoque como «independiente del lenguaje y del framework del servidor». La propia publicación usa un servidor Node sencillo, no Next.js ni TanStack Start.
Grabar respuestas reales con test-proxy-recorder
Sección titulada «Grabar respuestas reales con test-proxy-recorder»Con las tres opciones anteriores, escribes tú cada respuesta. test-proxy-recorder graba lo que devuelve tu API real durante una ejecución local y luego lo reproduce en CI.
- El proxy. Arranca
test-proxy-recorder <target-url>junto a tu app para la ejecución de las pruebas. Apunta la URL base de la API de la app hacia él mientrasTEST_PROXY_RECORDER_ENABLEDesté definida. Cada proxy reenvía a un solo backend, el<target-url>con el que se arrancó. registerProxyFetch(). Una llamada en el servidor parchea elfetchglobal. Copia la cabecerax-test-rcrd-idde la petición actual en cada petición saliente. El proxy lee esa cabecera para archivar cada llamada del lado del servidor bajo su prueba. La llamada es un no-op en producción salvo queTEST_PROXY_RECORDER_ENABLEDesté definida.- El id por prueba.
playwrightProxy.before(page, testInfo, mode)construye un id de sesión a partir del archivo de spec, sus títulos dedescribey el título de la prueba. Envía ese id comox-test-rcrd-iden las peticiones de la página y cambia esa sesión arecordoreplay. Los workers en paralelo comparten un mismo servidor de la app y un mismo proxy, y aun así cada prueba obtiene su propia grabación.
import { registerProxyFetch } from 'test-proxy-recorder/nextjs';
registerProxyFetch();import { registerProxyFetch } from 'test-proxy-recorder/tanstack-start';
registerProxyFetch();test.beforeEach(async ({ page }, testInfo) => { await playwrightProxy.before(page, testInfo, 'replay', { url: /localhost:8100/ });});Graba una vez contra la API real y haz commit de los archivos .mock.json (servidor) y .har (navegador). Después, CI los reproduce con el backend apagado. Al reproducir, una petición sin grabación recibe un 404 que la identifica. Nunca llega a la API.
Las respuestas grabadas solo cubren lo que hizo la API mientras grababas. Para forzar un error o un caso límite que la API no produce a petición, un handler escrito a mano con una de las opciones anteriores es más sencillo.
Los detalles de configuración están en las guías de Next.js y TanStack Start.
Comparar las opciones
Sección titulada «Comparar las opciones»| Opción | Configuración en la app | Pruebas en paralelo | Handlers o respuestas grabadas | Next.js y TanStack Start |
|---|---|---|---|---|
| Modo de prueba de Next.js | experimental.testProxy en next.config.js | Sí, los handlers se guardan por prueba | Handlers (next.onFetch() o handlers de MSW 2) | Solo Next.js |
| MSW en el servidor | setupServer arrancado en el código del servidor (el root layout en el ejemplo de MSW) | Un único conjunto de handlers para todas las pruebas de ese servidor | Handlers | Next.js: un ejemplo en un pull request abierto. TanStack Start: no documentado |
| mockttp | Sin cambios de código. Variables de proxy y un certificado de CA en el proceso del servidor | Sí, con un servidor de la app por worker | Handlers. Las peticiones sin regla se dejan pasar | No documentado. El equipo de Playwright lo considera independiente del framework |
| test-proxy-recorder | URL base de la API apuntando al proxy durante las pruebas, más registerProxyFetch() | Sí, con un servidor de la app compartido | Grabadas de la API real | Ambos documentados |
La comparación cubre más herramientas, entre ellas Mocky Balboa, scenarist, Polly.js, talkback y proxay, además de las opciones del lado del navegador.