vitest icon indicating copy to clipboard operation
vitest copied to clipboard

Vite/Vitest not picking up environment variables?

Open kevinmamaqi opened this issue 3 years ago • 14 comments

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

  1. Create react app with vite-react template
  2. Add vitest
  3. Create .env with some environment variables
  4. Create a file that uses the variable
  5. 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

kevinmamaqi avatar Oct 06 '22 06:10 kevinmamaqi

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 avatar Oct 06 '22 07:10 sheremet-va

@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 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 🤔

kevinmamaqi avatar Oct 06 '22 07:10 kevinmamaqi

Spent many hours trying to load basic environment variables and I'm still on it. Just saying...

DragonAxe01 avatar Jul 15 '23 16:07 DragonAxe01

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.

jamierytlewski avatar Aug 03 '23 14:08 jamierytlewski

import.meta { url: 'file:///C:/git/vite-project/src/App.tsx' } - This is the output that I'm getting from the import.meta.

import.meta.{env} is statically replaced, so you will never see it in there.

sheremet-va avatar Aug 03 '23 15:08 sheremet-va

@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.

chriswilty avatar Sep 13 '23 10:09 chriswilty

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 Where can I find this narrative in the official documentation, Want to confirm if only .env & .env.test is supported.

Michael-indochat avatar Sep 28 '23 02:09 Michael-indochat

@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

sheremet-va avatar Sep 28 '23 07:09 sheremet-va

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

shekhov avatar Nov 05 '23 17:11 shekhov

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(), ''),
    },
})

SiNONiMiTY avatar Jan 14 '24 10:01 SiNONiMiTY

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
    },
    
}));

taminhtienhai avatar May 26 '24 08:05 taminhtienhai

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.

sheremet-va avatar May 27 '24 15:05 sheremet-va

This fixed it for me:

import path from 'path'
import { loadEnvFile } from 'process'

loadEnvFile(path.resolve(__dirname, '.env'))

export default defineVitestConfig({/* */})

wesamjabali avatar May 29 '24 14:05 wesamjabali

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'),
    },
  },
  
 
})

Boby900 avatar Jun 30 '24 03:06 Boby900

@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({/* */})

githorse avatar Jul 06 '24 17:07 githorse

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 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'],
  }
}
});

ticup avatar Jul 18 '24 11:07 ticup

vitest --mode development

adding mode flag for vitest command in package.json is working.

surya-muruganantham avatar Jul 18 '24 13:07 surya-muruganantham