Environment variables
Loading "Environment Variables"
Run locally for transcripts
Another kind of global dependency your code may have is that on the environment. That is often done via environment variables, which, in JavaScript, are often exposed to the process via the
process.env
object.You reach out to environment variables in cases like managing API secrets, feature flags, implementing different environment-depending logic, etc. And as such, dependency on environment variable is also one you have to know how to mock to orchestrate your tests correctly.
One of the most common usages of environment variables is the dependency on the current process' environment, i.e.
process.env.NODE_ENV
. That's precisely what our Emitter
class does to imbue its methods with logging capabilities if run in development:function isDevelopment() {
return process.env.NODE_ENV === 'development'
}
export class Emitter<EventMap extends Record<string, Array<unknown>>> {
// ...
public on<Event extends keyof EventMap>(
event: Event,
listener: (...args: EventMap[Event]) => void,
): this {
if (isDevelopment()) {
console.log(`adding listener for "${event.toString()}" event`)
}
const prevListeners = this.listeners.get(event) || []
const nextListeners = prevListeners.concat(listener)
this.listeners.set(event, nextListeners)
return this
}
public emit<Event extends keyof EventMap>(
event: Event,
...data: EventMap[Event]
): boolean {
const listeners = this.listeners.get(event) || []
if (isDevelopment()) {
console.log(
`emitting listeners for "${event.toString()}" event (${
listeners.length
})`,
)
}
for (const listener of listeners) {
listener.apply(this, data)
}
return listeners.length > 0
}
}
First, it determines the environment on runtime via the
isDevelopment()
function, and then implements additional logic in the .on()
and .emit()
methods of the Emitter
based on result of the isDevelopment()
function call.But how do you test this?
You can, of course, set a different
NODE_ENV
environment variable on the test script itself:NODE_ENV=development npm test
But this won't play nicely on different operating systems, and will affect the entire test run instead of focusing on a single test suite. The worst part, you won't be able to swap the value of
NODE_ENV
only in some test cases to see how your code behaves.Alternatively, you can mock
process.env.NODE_ENV
as a global value, like you did in the previous exercise. That would work, but you would have not to forget to forward all the existing environment variables on process.env
before mocking your own variable or two. This can easily slip past your mind, resulting in unreliable and, at times, broken tests.You can also use
vi.stubEnv()
!π¨βπΌ The time has come for you to get to know
vi.stubEnv()
. In this exercise, complete new test cases for the test suite by mocking the value of process.env.NODE_ENV
using the built-in vi.stubEnv()
API in Vitest. Verify your solution by running npm test
. And remember, you will be asserting on console.log
calls too!