Vite/Vitest not picking up environment variables?
Describe the bug
I am trying to test a file that relies on environment variables, though it doesn't load them. I use vite+vitest with React.js. It works well on development/production, but when I test the component with the variable is undefined: import.meta.env.VITE_API_URL
When I initialize the test, it loads properly on my vite.config.js:
export default ({ mode }) => {
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }
console.log('mode', mode, loadEnv(mode, process.cwd())) <----- HERE IT APPEARS TO BE DEFINED WHEN THE TEST STARTS
return defineConfig({
base: '/',
server: {
port: 3000,
proxy: {
'/public': {
target: `${process.env.VITE_API_URL}/`,
changeOrigin: true,
},
},
},
publicDir: './public',
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/__test__/setup.js',
include: ['./src/__test__/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
})
}
Reproduction
- Create react app with vite-react template
- Add vitest
- Create .env with some environment variables
- Create a file that uses the variable
- Test the file and that the variable is present
Github repo: https://github.com/IT-Academy-BCN/ita-directory Branch: https://github.com/IT-Academy-BCN/ita-directory/tree/511-test-recover-password File: https://github.com/IT-Academy-BCN/ita-directory/blob/511-test-recover-password/frontend/src/test/pages/UserFlow/RecoverPassword.test.jsx
System Info
envinfo command not found
Used Package Manager
npm
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.
- [X] The provided reproduction is a minimal reproducible example of the bug.
Vitest runs with test mode, so it will not read .env.development by default (only .env and .env.test, so I'm not sure what you expect here 🤔
@sheremet-va As soon as you npm run frontend the .env.development is copied to a .env file in the frontend directory. Moreover, when the test start, the variables are defined:
console.log('mode', mode, loadEnv(mode, process.cwd())) <----- HERE IT APPEARS TO BE DEFINED WHEN THE TEST STARTS
Vitest runs with
testmode, so it will not read.env.developmentby default (only.envand.env.test, so I'm not sure what you expect here 🤔
Spent many hours trying to load basic environment variables and I'm still on it. Just saying...
Yeah, I'm having the same issue. It works when the environment is node. But if I'm using jsdom for the environment I'm not getting anything.
import.meta { url: 'file:///C:/git/vite-project/src/App.tsx' } - This is the output that I'm getting from the import.meta.
I have created an .env.test file that has VITE_APP_TITLE in it.
I have create an .env.test.local an .env.local.test and all the same issues.
The error message from the test is
TypeError: Cannot set properties of null (setting 'innerText')
❯ src/App.tsx:11:11
9| console.log("import.meta", import.meta);
10| const title = document.querySelector("title") as HTMLTitleElement;
11| title.innerText = `${import.meta.env.VITE_APP_TITLE}}`;
| ^
12| }, []);
This works in the browser, not on vitest.
import.meta { url: 'file:///C:/git/vite-project/src/App.tsx' }- This is the output that I'm getting from theimport.meta.
import.meta.{env} is statically replaced, so you will never see it in there.
@jamierytlewski, title is null according to the error message, it has nothing to do with your env var.
Plus you have a double curly brace at the end of your innerText value which is probably not what you want.
Vitest runs with
testmode, so it will not read.env.developmentby default (only.envand.env.test, so I'm not sure what you expect here 🤔
@sheremet-va Where can I find this narrative in the official documentation, Want to confirm if only .env & .env.test is supported.
@sheremet-va Where can I find this narrative in the official documentation, Want to confirm if only .env & .env.test is supported.
- https://vitejs.dev/guide/env-and-mode.html#env-files
- https://vitejs.dev/config/#using-environment-variables-in-config
The same issue here, but instead of using process.env I used vi.stubEnv. With the following test:
it ('Should fetch an api "/api/v1/hello-github"', async () => {
vi.stubEnv('VITE_API_BASE_ADDRESS', 'http://localhost:5000')
fetch.mockResponseOnce('Success', {status: 200});
inputField.value = validEmail;
await user.click(submitBtn);
expect(import.meta.env.VITE_API_BASE_ADDRESS).toBe('http://localhost:5000')
expect (fetch.requests().length).toEqual(1);
expect (fetch.requests()[0].url).toEqual('http://localhost:5000/api/v1/hello-github');
});
the test fails on the last expect, meaning that from vitest perspective it actually has loaded the env var, only later it did not pass it to the component under test
This will make Vitest load your .env file
// vitest.config.ts
import { loadEnv } from 'vite'
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
env: loadEnv('', process.cwd(), ''),
},
})
https://stackoverflow.com/a/78206557/11722137
import { defineConfig, mergeConfig } from 'vitest/config';
import viteConfig from './vite.config.js';
import { config } from "dotenv";
export default mergeConfig(viteConfig, defineConfig({
test: {
root: './tests',
env: {
...config({ path: ".env.development" }).parsed,
}, // this work for me
},
}));
stackoverflow.com/a/78206557/11722137
There is no need to install dotenv package since Vite exposes it via loadEnv already.
Vitest cannot load all envs because it would break Vite tests that rely on Vite loading only prefixed envs. If you still want to expose any env, you can use loadEnv method:
import { loadEnv } from 'vite'
import { defineConfig } from 'vitest/config'
export default defineConfig(({ mode }) => {
return {
test: {
env: loadEnv(mode, process.cwd(), '')
}
}
})
I think we should make it clear in the documentation before closing this issue.
This fixed it for me:
import path from 'path'
import { loadEnvFile } from 'process'
loadEnvFile(path.resolve(__dirname, '.env'))
export default defineVitestConfig({/* */})
this worked for me,
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import path from 'path'
import dotenv from "dotenv";
dotenv.config({ path: ".env.local" });
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})
@wesamjabali's answer is the only one that I can get to expose REACT_APP_ envars to the component under test:
import path from 'path' import { loadEnvFile } from 'process' loadEnvFile(path.resolve(__dirname, '.env')) export default defineVitestConfig({/* */})
stackoverflow.com/a/78206557/11722137
There is no need to install
dotenvpackage since Vite exposes it vialoadEnvalready.Vitest cannot load all envs because it would break Vite tests that rely on Vite loading only prefixed envs. If you still want to expose any
env, you can useloadEnvmethod:import { loadEnv } from 'vite' import { defineConfig } from 'vitest/config' export default defineConfig(({ mode }) => { return { test: { env: loadEnv(mode, process.cwd(), '') } } })I think we should make it clear in the documentation before closing this issue.
This works for me, but I also have a globalSetup file, and there the test environment variables from .env.test aren't loaded...
import { defineConfig } from 'vitest/config';
import { loadEnv } from 'vite'
export default defineConfig(({mode}) => {
return {
test: {
include: ['src/**/*.{test,spec}.{js,ts}'],
env: loadEnv(mode, process.cwd(), ''),
globalSetup: ['src/test/globalSetup.ts'],
}
}
});
vitest --mode development
adding mode flag for vitest command in package.json is working.