Mocking classes from mocked modules in Vitest tests

How to mock and expect on class methods on classes from vi.mocked modules.

typescript
import { describe, it, vi } from 'vitest'
import { ClassToMock } from './path/to/class/to/mock'
 
// This line mocks the module so the actual class method doesn't get called
vi.mock('./path/to/class/to/mock')
 
describe('ThingThatUsesClassToMock', () => {
  it('should do the thing that I expect it to do :)', t => {
    const methodSpy = vi.spyOn(
      // Spy on the class' prototype since that's where the actual function is.
      // If you're mocking a static method, you can use the class directly instead
      // of its prototype.
      ClassToMock.prototype,
      // The name of the method to expect on
      'method'
    )
 
    // do the thing!!
 
    // expect based on the spy just like you'd expect on a mock
    t.expect(methodSpy).toHaveBeenCalledOnce()
  })
})

Warning

This method will only work if the method is defined using the Method Definition syntax, and not as a property.

For example, this is valid:

typescript
class ValidClassToMock {
  // 
  methodThatWorks() {}
}

but theses aren't:

typescript
class InvalidClassToMock {
  // 
  arrowFunctionThatDoesNotWork = () => {}
  // 
  functionKeywordThatDoesNotWork = function () {}
}

This is because only the first method defines the class on the prototype of the class (and so the functions are shared between instances). The last two methods are created per-instance and are not defined on the class' prototype.

Created 13/02/24Updated 04/03/24
Found a mistake, or want to suggest an improvement? Source on GitHub here
and see edit history here