(This was not apparent at all despite an hour or two looking through Vitest’s docs…)

tldr

On StackOverflow, Ben Butterworth succintly solved this Vitest predicament in an answer to a question about Jest.

// a-local-file.js -----------------------------------------------
import {dependency} from './deps'

export function aFunction() {
    setTimeout(() => {
        dependency()
    }, 2000)
}

// test.js -------------------------------------------------------
import {test, vi} from 'vitest'
import {aFunction} from './a-local-file'
import {dependency} from './deps'

vi.mock('./deps')

describe('when it returns true', () => {
    beforeEach(() => {
        vi.mocked(dependency).mockResolvedValue(true)
    })
    test('...', () => {/* ... */})
})
describe('when it returns false', () => {
    beforeEach(() => {
        vi.mocked(dependency).mockResolvedValue(true)
    })
    test('...', () => {/* ... */})
})

Why??

I was adding tests to a proof-of-concept TypeScript function running on AWS Lambda. The main function is in the file index.ts.

It uses import to pull in a helper login function, in the file login-helper.ts.

I wanted to test the behavior of the index.ts function when the login fails, as well as when it works.

With this ability to conditionally mock the extracted login function, I can have one describe block where I test the failing-login behavior…

import {describe, test, vi} from 'vitest'
import {login} from './login-helper'
import {handlePayload} from './index'
vi.mock('./login-helper')
describe('handlePayload', () => {
  // ...
  describe('with invalid login', () => {
    beforeEach(() => {
      vi.mocked(login).mockRejectedValue('mocked login fails')
    })
    test('throws', async () => {
      await expect(() => handlePayload(payload)).rejects.toThrow('mocked login fails')
    })
  })
})

…as well as the “happy path” logic when the login succeeds…

import {describe, test, vi} from 'vitest'
import {login} from './login-helper'
import {handlePayload} from './index'
vi.mock('./login-helper')
describe('handlePayload', () => {
  // ...
  describe('with valid login', () => {
    let mockedPost
    beforeEach(() => {
      mockedPost = vi.fn()
      vi.mocked(login).mockResolvedValue({
        post: mockedPost,
      })
    })
    test('attempts to post', async () => {
      await handlePayload(payload)
      expect(mockedPost).toHaveBeenCalledWith({/* ... */})
    })
  })
})

Updated: