Environment variables
Loading "Environment Variables (🏁 solution)"
Run locally for transcripts
There are multiple mocks working together in this test suite, so I make sure to establish them (and their cleanup) accordingly:
beforeAll(() => {
vi.spyOn(console, 'log').mockImplementation(() => {})
})
afterEach(() => {
vi.resetAllMocks()
vi.unstubAllEnvs()
})
afterAll(() => {
vi.restoreAllMocks()
})
I'm using
vi.spyOn(console, 'log')
to be able to assert on console.log
calls in test, but the most important part is that I'm clearing any environment variable mocks by adding vi.unstubAllEnvs()
to the afterEach()
hook.I'm using theafterEach()
hook here so that individual test cases could have different values for the same environment variables.
In the first test suite, the intention is to make sure that our
Emitter
class prints messages to the console when process.env.NODE_ENV
is "development"
. I will mock the value of that environment variable using vi.stubEnv()
as the first thing in the test:test('logs debugging messages when run in development', () => {
vi.stubEnv('NODE_ENV', 'development')
Note that it's possible (and sometimes even beneficial) to have a combination of both global (thebefore*
andafter*
hooks) and local test setups, like we have here with thevi.stubEnv()
function insise this test.
The
vi.stubEnv()
utility accepts two arguments:- A variable name to stub (e.g.
NODE_ENV
); - A mock value.
You can imagine the
vi.stubEnv
function doing the following:function stubEnv(variableName, value) {
process.env[variableName] = value
}
However, unlike this simplified example, the actual function also allows you to restore the mocked variables without having to manually keep track of their original values.
With that, this test case is configured, and the only thing remaining is to act and assert:
test('logs debugging messages when run in development', () => {
vi.stubEnv('NODE_ENV', 'development')
const emitter = new Emitter<{ hello: [firstName: string] }>()
const listener = vi.fn()
emitter.on('hello', listener)
expect(console.log).toHaveBeenCalledWith(
`adding listener for "hello" event...`,
)
emitter.emit('hello', 'John')
expect(console.log).toHaveBeenCalledWith(
`emitting listeners for "hello" event (1)`,
)
})
Here, I'm adding new event listeners and emitting events, following those actions with assertions to make sure
console.log
is called appropriately in both cases.The second test case will be quite similar:
- Mock the value of
process.env.NODE_ENV
; - Add listeners/emit events;
- Assert that
console.log
has NOT been called.
test('does not log debugging messages in production', () => {
vi.stubEnv('NODE_ENV', 'production')
const emitter = new Emitter<{ hello: [firstName: string] }>()
const listener = vi.fn()
emitter.on('hello', listener)
expect(console.log).not.toHaveBeenCalled()
emitter.emit('hello', 'John')
expect(console.log).not.toHaveBeenCalled()
})
Although this test case will pass even if you omit thevi.stubEnv()
setup, that won't be correct. It passes because Vitest setsprocess.env.NODE_ENV
to"test"
by default, but your intention is not to test howEmitter
behaves in test. It is to check how it behaves in production.