Por qué vi.stubEnv no funciona con import.meta.env.DEV
Este me costó una sesión de debugging. Post corto, pero vale la pena escribirlo.
El Problema
Tenés código que ramifica según import.meta.env.DEV:
export function isDraftInDevMode(data: { draft?: boolean }): boolean {
return import.meta.env.DEV && data.draft === true
}
Querés testear ambas ramas, así que usás vi.stubEnv:
it('retorna false en producción', () => {
vi.stubEnv('DEV', 'false') // ← parece correcto
expect(isDraftInDevMode({ draft: true })).toBe(false)
})
El test pasa en CI. Pasa en local. Pero la aserción es una mentira — la función en realidad está retornando true.
Por Qué
vi.stubEnv funciona seteando valores en process.env (y en import.meta.env para el surface de env de Vite). Los valores son siempre strings — ese es el contrato de las variables de entorno.
El flag DEV de Vite es especial. En build time, Vite reemplaza import.meta.env.DEV con el literal booleano true o false. Pero en test time en Vitest, ese reemplazo no ocurre — import.meta.env es un objeto mutable y DEV se mantiene como sea que fue inicializado.
Entonces cuando llamás vi.stubEnv('DEV', 'false'), estás seteando import.meta.env.DEV al string 'false'. Y el string 'false' es truthy en JavaScript. Tu rama de modo producción nunca se ejecuta de verdad.
vi.stubEnv('DEV', 'false')
console.log(typeof import.meta.env.DEV) // "string"
console.log(!!import.meta.env.DEV) // true — 'false' es truthy!
El Fix
Saltate vi.stubEnv para los flags de entorno booleanos y asigná directamente:
import.meta.env.DEV = false // modo producción
import.meta.env.DEV = true // modo dev
En el entorno de tests de Vitest, import.meta.env es un objeto mutable plano. La asignación directa funciona exactamente como esperás:
it('retorna false en producción', () => {
import.meta.env.DEV = false
expect(isDraftInDevMode({ draft: true })).toBe(false) // realmente false ahora
})
it('retorna true en dev con draft=true', () => {
import.meta.env.DEV = true
expect(isDraftInDevMode({ draft: true })).toBe(true)
})
Cleanup
Si estás seteando import.meta.env.DEV en múltiples tests, resetealo en afterEach para que los tests no se contaminen entre sí:
const originalDEV = import.meta.env.DEV
afterEach(() => {
import.meta.env.DEV = originalDEV
})
O si todo tu bloque describe testea comportamiento en producción, setealo una vez en beforeAll:
describe('modo producción', () => {
beforeAll(() => { import.meta.env.DEV = false })
afterAll(() => { import.meta.env.DEV = true })
it('filtra drafts', () => { /* ... */ })
it('filtra fechas futuras', () => { /* ... */ })
})
Cuándo Usar vi.stubEnv
vi.stubEnv es correcto para env vars de tipo string — RESEND_API_KEY, POSTGRES_URL, cualquier cosa que venga de archivos .env. Esas siempre son strings y vi.stubEnv maneja limpiamente el ciclo de vida de restore-on-cleanup.
El problema ocurre solo con DEV, PROD, SSR y MODE — los flags específicos de Vite que son booleans/strings que se reemplazan estáticamente en build time pero quedan como objetos mutables en los tests.




Comments for mprtmt