Testare Workers con Jest in esecuzione su Node.js è una trappola elegante. I test vengono superati, l'elemento della configurazione diventa verde e il codice arriva in produzione con bug che l'ambiente di test non potrebbe mai rilevare, perché l'ambiente di test non è il runtime che esegue il codice in produzione. I lavoratori vengono eseguiti su isolati V8 con le API della piattaforma Web, non le API Node.js: non c'è Buffer, non c'è process.env, fetch è l'implementazione nativa del runtime (non node-fetch né undici) e crypto è l'API Web Crypto con le sue firme asincrone. Un mock fetch scritto per Jest in Node.js potrebbe superare i test e silenziosamente non funzionare quando il Worker ha a che fare con il vero Response dal limite.
Perché l'ambiente di test è più importante di quanto sembri
La differenza tra Node.js e il runtime Workers va oltre le API disponibili: il comportamento edge appare solo in condizioni specifiche. Il TextEncoder in Node.js e il TextEncoder nel runtime Workers hanno la stessa interfaccia, ma il Uint8Array che producono potrebbe comportarsi diversamente quando passato a un'operazione SubtleCrypto a seconda della versione e dell'implementazione. Workers Headers ha un comportamento di normalizzazione del nome dell'intestazione (ordine lessicografico senza distinzione tra maiuscole e minuscole) che differisce dalle implementazioni fittizie.
Ancora più importante: i collegamenti non esistono in Node.js. env.KV, env.DB, env.BUCKET sono oggetti che il runtime inserisce: non c'è modo di importare un pacchetto npm che fornisca loro la massima fedeltà. I mock manuali testano il codice che chiama il mock, non il codice che funzionerà con l'associazione effettiva.
@cloudflare/vitest-pool-workers risolve questo problema eseguendo i test all'interno di un vero runtime Workers, in particolare Miniflare v3, che esegue un vero isolato V8, non una simulazione Node.js. I test hanno accesso a caches, crypto, fetch, Headers, ReadableStream, TransformStream con implementazioni runtime. I collegamenti vengono forniti come implementazioni in-process: KV in memoria, D1 come SQLite incorporato, R2 come archiviazione locale. Il risultato è che un test che supera con vitest-pool-workers sta testando il codice che funzionerà nel runtime reale.
Configurazione di vitest.config.ts con i collegamenti
La configurazione del pool di lavoratori in vitest.config.ts è dove dichiari i vincoli che i test riceveranno. La struttura rispecchia wrangler.toml, ma nella configurazione Vitest:
import { defineConfig } from 'vitest/config'; import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'; export default defineWorkersConfig({ test: { poolOptions: { workers: { wrangler: { configPath: './wrangler.toml' }, miniflare: { kvNamespaces: ['CACHE'], d1Databases: ['DB'], r2Buckets: ['BUCKET'], bindings: { APP_ENV: 'test', }, }, }, }, }, });
Con questa configurazione, ogni suite di test riceve un ambiente pulito con uno spazio dei nomi KV vuoto, un database SQLite D1 vuoto (con lo schema applicato se si configura migrations) e un bucket R2 vuoto. I collegamenti sono isolati tra le suite: le modifiche apportate dai test in una suite non vengono diffuse in un'altra.
Per accedere ai collegamenti nei test, @cloudflare/vitest-pool-workers esporta un helper env che Miniflare inietta tramite un meccanismo globale:
import { env } from 'cloudflare:test'; test('deve retornar 404 para usuário inexistente', async () => { const request = new Request('https://api.example.com/users/999'); const response = await SELF.fetch(request); expect(response.status).toBe(404); });
SELF è l'associazione del lavoratore stesso sotto test: puoi eseguire SELF.fetch() per testare il gestore di recupero come una chiamata HTTP di integrazione, con instradamento completo e tutti i collegamenti disponibili.
La divisione tra test unitari e test di integrazione
Le funzioni di logica aziendale che operano su valori JavaScript puri (analisi di un JWT, calcolo di un prezzo scontato, convalida di uno schema di input) possono e devono essere testate senza il pool di lavoratori. Queste funzioni non dipendono dalle API o dai collegamenti di runtime e testarle in Node.js puro con Vitest standard è più rapido e semplice. La regola pratica: se la funzione accetta e restituisce valori primitivi JavaScript o oggetti flat, appartiene al pool Node.js. Se usa Request, Response, Headers, ReadableStream o qualsiasi vincolo, appartiene alla riserva Lavoratori.
I gestori HTTP di integrazione, ovvero la funzione fetch(request, env, ctx) che è il punto di ingresso di Worker, necessitano che il pool di Worker venga testato fedelmente. Testare un gestore di integrazione significa andare a SELF.fetch(new Request(...)) e controllare lo stato, le intestazioni della risposta e il corpo della risposta che il Worker produrrebbe in produzione. Questo test ha anche un effetto collaterale sulle associazioni: se il gestore dovesse salvare un record in D1, il test può essere eseguito env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first() più tardi e verificare che i dati siano stati scritti correttamente.
CI senza credenziali Cloudflare e i limiti di Miniflare
vitest-pool-workers con Miniflare viene eseguito in modo completamente locale: non effettua alcuna chiamata all'API Cloudflare, non necessita di CLOUDFLARE_ACCOUNT_ID o CLOUDFLARE_API_TOKEN nel CI. Miniflare v3 scarica workerd (il runtime open source di Cloudflare) come binario locale e lo esegue come qualsiasi altro processo. La pipeline CI si riduce a npm ci e npx vitest run: nessun segreto, nessuna dipendenza dalla rete oltre al download binario alla prima installazione.
Miniflare v3 simula il runtime con alta fedeltà, ma esistono eccezioni documentate che contano per la strategia di test. Gli oggetti durevoli in modalità di produzione, con la relativa garanzia singleton globale, non vengono simulati con coerenza distribuita; Miniflare crea un'istanza locale utile per la logica di base, ma non testa il comportamento di coordinamento effettivo. I comportamenti CDN come l'edge caching e l'oggetto cf con dati di geolocalizzazione reali necessitano di wrangler dev --remote per essere esercitati.
In questi casi, la risposta sta nell'organizzare test del fumo che effettuano chiamate HTTP dirette a un lavoratore di staging dopo la distribuzione. Non sostituiscono i test Miniflare: coprono ciò che Miniflare non può simulare: comportamenti che esistono solo quando il codice viene eseguito su un'infrastruttura reale.
Test di waitUntil e funzionamento in background
Il ctx.waitUntil() è dove avviene il lavoro dopo che la risposta è stata inviata. Il SELF.fetch() nei test restituisce immediatamente la risposta, ma il lavoro passato a waitUntil potrebbe non essere finito. @cloudflare/vitest-pool-workers fornisce waitOnExecutionContext per risolvere questo problema:
import { env, SELF, waitOnExecutionContext } from 'cloudflare:test'; test('deve enfileirar analytics em background', async () => { const ctx = createExecutionContext(); const response = await SELF.fetch(new Request('https://api.example.com/checkout')); await waitOnExecutionContext(ctx); const queued = await env.ANALYTICS_QUEUE.read(); expect(queued).toHaveLength(1); });
Senza waitOnExecutionContext, un test che verifica l'effetto collaterale di waitUntil avrà una condizione di competizione: instabilità difficile da diagnosticare perché il problema non è nel codice di produzione, ma nel codice di test.
Leggi anche
- Operatori Cloudflare in produzione: cosa cambia dopo hello world
- Lavoratori + D1 + KV + R2: comporre associazioni nello stesso servizio
- Workers: debug, log e Workers Tail: osservabilità all'edge senza un server di log
- Lavoratori: limiti di CPU e memoria - che la documentazione non spiega bene
- Sviluppo di applicazioni serverless con AWS Lambda e Cloudflare Workers nel 2025
- Operatori Cloudflare: Guida pratica all'Edge Computing Serverless
