I would like to raise this issue again, as I have not been able to find an acceptable solution. If you feel it is not related to this project please close it and I will stop the pestering ๐
I am using environment variables as explained here: https://vuejs-templates.github.io/webpack/env.html
When I run vue-test-utils tests with Jest, the environment variables are not loaded. I believe this is because the tests don't use Webpack, please correct me if I am wrong.
I have created a project to show the issue: https://github.com/DarynHolmes/vue-jest-example
Use npm run unit to see the failing test.
Is there any recommended approach to solving this issue?
Again, sorry for the repost. If this is not the right project, could you please point me in the right direction?
Thanks, Daryn
Hey,
You can see here https://facebook.github.io/jest/docs/en/configuration.html
Jest uses it's own config, you can make sure own and specify the location in the jest section of your package.json.
The Vue environment variables are used with
process.env.EXAMPLE
@lmiller1990 do you have a suggestion on how I can make sure I have such an object available during the Jest test run? Which one of the options do I need to use? setupFiles? Globals?
Any chance you could provide example code?
Thanks
I'm sorry Daryn, this really is outside the scope of vue-test-utils.
Since this issue shows up high in search results for setting environment variables for Jest tests, I'll post what I found for posterity. You can simply pass the environment variables on the command line when invoking Jest, like so:
EXAMPLE=foo jest
thanks @thinkjson that worked for me. I'm using Gatsby for a project and had to update the process.env
"test": "GATSBY_API=https://api-url.co.uk jest"
I had luck with
process.env = Object.assign(process.env, { CUSTOM_VAR: 'value' });
in my beforeEach()
Since this issue shows up high in search results for setting environment variables for Jest tests, I'll post what I found for posterity. You can simply pass the environment variables on the command line when invoking Jest, like so:
EXAMPLE=foo jest
This breaks for non-Unix or Linux related systems. My friend just tested this with Windows.
I had luck with
process.env = Object.assign(process.env, { CUSTOM_VAR: 'value' });in my
beforeEach()
Better to put this in beforeAll that way it runs once. See: https://jestjs.io/docs/en/setup-teardown Just tested this for the internationalization and that fails the tests. Here's my spec:
import DateFormatterService from "../date-formatter-service";
beforeAll(() => {
process.env = Object.assign(process.env, { NODE_ICU_DATA: 'node_modules/full-icu' });
});
// Format of a Date object is: new Date(year, month, day, hours, minutes, seconds, milliseconds)
describe("DateFormatterService", () => {
it("Should format default date for en-US", () => {
const DFS = new DateFormatterService();
const targetDate = new Date(2019, 2, 13);
const formattedDate = DFS.formatDate(targetDate);
expect(new Date(formattedDate)).toEqual(targetDate);
});
it("Should format date with leading zeros for day and month and numeric for year en-US", () => {
const DFS = new DateFormatterService();
const targetDate = new Date(2019, 2, 13);
const formattedDate = DFS.formatDate(targetDate, {
day: "2-digit",
year: "numeric",
month: "2-digit"
});
const targetDateStr =
(targetDate.getMonth().toString().length > 1
? targetDate.getMonth() + 1
: "0" + (targetDate.getMonth() + 1)) +
"/" +
(targetDate.getDate().toString().length > 1
? targetDate.getDate()
: "0" + targetDate.getDate()) +
"/" +
targetDate.getFullYear();
expect(formattedDate).toEqual(targetDateStr);
});
it("Should format date with locale of en-GB", () => {
const DFS = new DateFormatterService();
const targetDate = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
const month =
targetDate.getMonth().toString().length > 1
? targetDate.getMonth() + 1
: "0" + (targetDate.getMonth() + 1);
const day =
targetDate.getDate().toString().length > 1
? targetDate.getDate()
: "0" + targetDate.getDate();
const year = targetDate.getFullYear();
// British date's default to the format of: dd/mm/yyyy USA format is: mm/dd/yyyy
const targetDateStr = `${day}/${month}/${year}`;
const formattedDate = DFS.formatDate(targetDate, {}, "en-GB");
expect(formattedDate).toEqual(targetDateStr);
});
// // Note: this test might break if you happen to be in the window of time where the day is the same, but generally Japan is one day ahead of the USA.
it("Should format date with Tokyo, Japan timezone and locale", () => {
const DFS = new DateFormatterService();
const targetDate = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
const formattedDate = DFS.formatDate(
targetDate,
{ timeZone: "Asia/Tokyo" },
"ja-JP"
);
// Add a day to the target date string for the comparison
const month =
targetDate.getMonth().toString().length > 1
? targetDate.getMonth() + 1
: "0" + (targetDate.getMonth() + 1);
const day =
targetDate.getDate().toString().length > 1
? targetDate.getDate() + 1
: "0" + targetDate.getDate() + 1;
const year = targetDate.getFullYear();
// Japanese date's default to the format of: yyyy/mm/dd USA format is: mm/dd/yyyy
const targetDateStr = `${year}/${month}/${day}`;
expect(formattedDate).toEqual(targetDateStr);
});
it("Should throw an exception when a bogus locale of ja_JP is supplied", () => {
const DFS = new DateFormatterService();
const targetDate = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
// This function is meaningless but should throw an exception
function formatDateInvalid() {
DFS.formatDate(targetDate, { timeZone: "Asia/Tokyo" }, "ja_JP");
}
expect(formatDateInvalid).toThrowError(/language tag/);
});
it("Should format date in German long format", () => {
const DFS = new DateFormatterService();
const targetDate = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
let options = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
};
const formattedDate = DFS.formatDate(targetDate, options, "de-DE");
// German is minus one day to the USA (MST) so December 19, 2012
expect(formattedDate).toEqual("Mittwoch, 19. Dezember 2012");
});
});
Here is the date service:
/***
* This is a wrapper (decorator) fo the Intl.DateTimeFormat and provides access to
* the intl date and locale settings. There's a unit spec file for this API. Please
* refer to that for a myriad of different API settings for dates etc.
*/
class DateFormatterService {
/**
* The options format the date. See MDN Intl.DateTimeFormat
*
* @param date
* @param options
* @param locale
* @returns new date in String format
*/
formatDate(date, options = {}, locale) {
return new Intl.DateTimeFormat(locale, options).format(date);
}
/**
* Format date to parts:
*
* // return value:
* [
* { type: 'weekday', value: 'Monday' },
* { type: 'literal', value: ', ' },
* { type: 'month', value: '12' },
* { type: 'literal', value: '/' },
* { type: 'day', value: '17' },
* { type: 'literal', value: '/' },
* { type: 'year', value: '2012' },
* { type: 'literal', value: ', ' },
* { type: 'hour', value: '3' },
* { type: 'literal', value: ':' },
* { type: 'minute', value: '00' },
* { type: 'literal', value: ':' },
* { type: 'second', value: '42' },
* { type: 'literal', value: ' ' },
* { type: 'dayPeriod', value: 'AM' }
* ]
*
* @param date
* @param options
* @param locale
* @returns {*}
*/
formatDateToParts(date, options = {}, locale) {
return new Intl.DateTimeFormat(locale, options).formatToParts(date);
}
getLCRFDate(date, locale = "en-US") {
let day, month, year;
day = month = year = "";
this.formatDateToParts(
new Date(date),
{
day: "numeric",
month: "short",
year: "numeric"
}, locale).map(({type, value}) => {
return {[type]: value};
}
).forEach(p => {
// There's just one key to check
let k = Object.keys(p)[0];
debugger;
switch(k) {
case "day":
day = p[k];
break;
case "month":
month = p[k];
break;
case "year":
year = p[k];
break;
}
});
return `${day} ${month} ${year}`;
}
}
export default DateFormatterService;
Better to put this in beforeAll that way it runs once. See: https://jestjs.io/docs/en/setup-teardown Just tested this for the internationalization and that fails the tests. Here's my spec:
```
import DateFormatterService from "../date-formatter-service";beforeAll(() => {
process.env = Object.assign(process.env, { NODE_ICU_DATA: 'node_modules/full-icu' });
});Even simpler: I added the following line (from above) to my
jest.config.jsfile, just after the definition ofmodule.exports:
process.env = Object.assign(process.env, { NODE_ICU_DATA: 'node_modules/full-icu' });
Did that to migrate a relatively large set of unit tests using toLocaleString (among other things) from Karma / Jasmine, in an Angular project, thus running Jest from angular.json, not from package.json, so not able to use the classical NODE_ICU_DATA=node_modules/full-icu jest trick (which might fail in Windows without the cross-env lib, anyway).
let code = require('path/to/mycode');
require('dotenv').config();
test('Code Using Environment Variables works', () => {
expect(code.getResult()).toEqual('the result');
});
One can also simply add: require('dotenv').config(); to jest.config.js.
One can also simply add:
require('dotenv').config();tojest.config.js.
I like how each previous reply got incrementally closer to the correct answer, which is this one.
@SimonLegg config method accepts options argument and one of the properties is path - basically you can give it a path to a custom .env file:
Default:
path.resolve(process.cwd(), '.env')
You may specify a custom path if your file containing environment variables is located elsewhere.
require('dotenv').config({ path: '/full/custom/path/to/your/env/vars' })
I had luck with
process.env = Object.assign(process.env, { CUSTOM_VAR: 'value' });in my
beforeEach()
I got this error โโโโ
ReferenceError: Invalid left-hand side in assignment
https://stackoverflow.com/a/52872875/796919
I fixed it by use env file.
"test:unit": "NODE_ENV=test vue-cli-service test:unit --mode production",
In .env.production file:
VUE_APP_SECRET=xxxxxxxxx
One can also simply add:
require('dotenv').config();tojest.config.js.I like how each previous reply got incrementally closer to the correct answer, which is this one.
Putting it in the jest config is not the correct answer in a lot of cases (more specific: when dealing with multiple environments where you want to test the behaviour of an environment with other settings)
This simple approach also works:
it('my test', async () => {
// First, set the env var
process.env.MY_ENV_VAR = 'abc';
// Next, load the module โ do it dynamically, not at the top of the file!
const functionToTest = (await import('./mymodule')).functionToTest;
// Test code as usual
expect(functionToTest()).toBe('abc');
});
(In my specific case, the output of functionToTest depends on a value in MY_ENV_VAR so it's beneficial to have it as close to the test code as possible.)
https://github.com/facebook/jest/blob/master/packages/jest-cli/bin/jest.js#L12-L14
Find on node_module/jest-cli/bin/jest.js
#!/usr/bin/env node
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const importLocal = require('import-local');
if (!importLocal(__filename)) {
if (process.env.NODE_ENV == null) {
process.env.NODE_ENV = 'test';
}
require('../build/cli').run();
}
I'll add this for when I inevitably run into this issue again...
I had a method:
import jwt from "jsonwebtoken";
import { IAccountDocument } from "../interfaces/IAccount";
const { JWT_SECRET, JWT_EXPIRES_IN } = process.env;
const createToken = (account: IAccountDocument) => {
const { cardId } = account;
const access_token = jwt.sign({ id: cardId }, JWT_SECRET!, {
expiresIn: JWT_EXPIRES_IN, // 5 mins
});
return {
access_token,
token_type: "bearer",
expires_in: parseInt(JWT_EXPIRES_IN!, 10),
};
};
When I was manually testing, it worked fine, but running automated tests, the JWT_SECRET and JWT_EXPIRES_IN weren't being pulled in during the test.
Turns out I needed to have const { JWT_SECRET, JWT_EXPIRES_IN } = process.env; _inside_ the createToken method ๐๐
Set in jest.config.js
"jest": {
"setupFiles": [
"<rootDir>/{your_test_folder}/setup.js"
]
}
Create setup.js under {your_test_folder} and add the following
process.env.YOUR_VAR = 'value';
you can also make your own choices inside setup.js since it will be loaded
as described here https://jestjs.io/docs/en/configuration#setupfiles-array
I'll add this for when I inevitably run into this issue again...
I had a method:
import jwt from "jsonwebtoken"; import { IAccountDocument } from "../interfaces/IAccount"; const { JWT_SECRET, JWT_EXPIRES_IN } = process.env; const createToken = (account: IAccountDocument) => { const { cardId } = account; const access_token = jwt.sign({ id: cardId }, JWT_SECRET!, { expiresIn: JWT_EXPIRES_IN, // 5 mins }); return { access_token, token_type: "bearer", expires_in: parseInt(JWT_EXPIRES_IN!, 10), }; };When I was manually testing, it worked fine, but running automated tests, the
JWT_SECRETandJWT_EXPIRES_INweren't being pulled in during the test.Turns out I needed to have
const { JWT_SECRET, JWT_EXPIRES_IN } = process.env;_inside_ thecreateTokenmethod ๐๐
Is there a work around for this? without having to nest variables inside of functions?
EDIT:
I fixed variables being undefined outside of function scope. For me I had my jest.config.js file in an subfolder in root dir. So the config.js file was never being read. And therefore none of the setupEnv files were running so the env variables were never set. Bringing my jest.config.js file out to root level fixed everything for me.
I had luck with
process.env = Object.assign(process.env, { CUSTOM_VAR: 'value' });in my
beforeEach()
Thanks @nineohnine
Would anyone suggest if our variable is array (csv values)? i.e. VAR=3,4,5
Because it seems JEST read this as "3,4,5" instead. (or Vue directly)?
Thank you
I had luck with
process.env = Object.assign(process.env, { CUSTOM_VAR: 'value' });in my
beforeEach()Thanks @nineohnine
Would anyone suggest if our variable is array (csv values)? i.e.VAR=3,4,5Because it seems JEST read this as "3,4,5" instead. (or Vue directly)?
Thank you
Why not do string.split(',')
Thanks @rcg-dev
To put more context, Im using Nuxt and use separate file for env config. I did make splitting there.
The app did run and convert it to array successfully. But, test run (Jest) is still get it by string. So, I suspect the config file we make in nuxt is not executed in Jest.
May I know if you guys have suggestions pls? I did use Jest setupFiles and setupFilesAfterEnv but it still not executed apparently
Thanks!
Thanks @rcg-dev
To put more context, Im using Nuxt and use separate file for env config. I did make splitting there.
The app did run and convert it to array successfully. But, test run (Jest) is still get it by string. So, I suspect the config file we make in nuxt is not executed in Jest.
May I know if you guys have suggestions pls? I did use Jest
setupFilesandsetupFilesAfterEnvbut it still not executed apparentlyThanks!
Did you make sure that your jest-config.js is in the root folder and does it look something like this?
module.exports = {
setupFiles: ['<rootDir>/env.js'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']}
Since this issue shows up high in search results for setting environment variables for Jest tests, I'll post what I found for posterity. You can simply pass the environment variables on the command line when invoking Jest, like so:
EXAMPLE=foo jestThis breaks for non-Unix or Linux related systems. My friend just tested this with Windows.
You need cross-env to make that solution work on Windows
Most helpful comment
I had luck with
in my
beforeEach()