Global methods
Loading "Global Methods (๐ solution)"
Run locally for transcripts
I start by going to the
beforeAll()
hook and adding a spy on the console.log
method using the vi.spyOn()
utility:beforeAll(() => {
vi.spyOn(console, 'log').mockImplementation(() => {})
})
I'm overriding the implementation of
console.log
for this test suite by using .mockImplementation()
and providing it an empty function. With this, I can still assert on the console.log
calls without it actually printing anything to the terminal output.From this moment forward, all
console.log
calls are recorded and I can assert on them in any test!Next, I need to reset the recorded state of the spy function for each test run. I will use
vi.resetAllMocks()
for that:afterEach(() => {
vi.resetAllMocks()
})
vi.resetAllMocks()
targets all mocks created in this test suite, not just the spy forconsole.log
. Instead of resetting an individual spy, I prefer resetting all mocks because none of them should leak their state to irrelevant tests.
And, finally, restore the original implementation of
console.log
once the tests are done:afterAll(() => {
vi.restoreAllMocks()
})
This concludes the testing setup changes but we still have some assertions to adjust.
Since I am spying on a global method, I can write assertions on that global method directly:
expect(console.log).toHaveBeenCalledWith(
'Sever is listening at http://127.0.0.1/',
)
expect(console.log).toHaveBeenCalledOnce()
expect(console.log).toHaveBeenCalledWith(
'Sever is listening at http://127.0.0.1:5639/',
)
expect(console.log).toHaveBeenCalledOnce()
The only thing remaining is to run the tests and make sure they are passing:
โ print-server-url.test.ts (2)
โ prints the server message for url with host and no port
โ prints the server message for url with host and port
Bonus: Type-safe global spies
Although Vitest reassigns the value of
console.log
to a spy function on runtime, TypeScript doesn't know about that. In fact, if you attempt to read some of the spy function's properties, you'll be greeted with a type error:console.log.mock.calls
// Property 'mock' does not exist on type '...'
To fix this, you need to extend the type of
Console['log']
in your test and mark it as a mock instance.import { MockInstance } from 'vitest'
declare namespace console {
var log: Console['log'] &
MockInstance<Parameters<Console['log']>, ReturnType<Console['log']>>
}
console.log.mock.calls
// [["Server is listening at http://localhost:5639/"]]
The way you extend the global type will depend on the declaration for that type. The type of
console.log
happened to be declared in a global namespace console
so that's the namespace we extend above. Inspect the type declaration of the global you want to extend to ensure it's done correctly.