Global methods
Loading "Global Methods (🏁 solution)"
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 portBonus: 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.