I'm trying to write tests for a class that uses dependency injection (DI) and I'm using the vitest testing framework. The class I want to test is CastVoteCommandHandler, which has a dependency on a VoteRepository. I want to mock the VoteRepository for testing purposes.
@injectable()
export class CastVoteCommandHandler {
public constructor(
@inject(symbols.voteRepository)
private readonly voteRepository: VoteRepository
) {}
public async execute(payload: CastVoteData) {
if (await this.voteRepository.hasVotedTwiceOnAnswer(payload)) {
throw new VoteProcessingError('You can only vote once on the same answer.')
}
if (!(await this.voteRepository.checkIfAnswerBelongsToPool(payload))) {
throw new VoteProcessingError('Answer does not exist in the current pool.')
}
const existingVote = await this.voteRepository.findVoteInPoolByVoter(payload)
if (existingVote) {
const { vote } = existingVote
await this.voteRepository.updateVote({
voteId: vote.getId(),
answerId: payload.answerId,
})
} else {
await this.voteRepository.createVote(payload)
}
SocketIOService.emitVote(payload.poolId)
}
}
I want to write a test for the execute method of CastVoteCommandHandler using the vitest testing framework. Here's the test code I've written:
import { faker } from '@faker-js/faker'
import { ContainerSingleton } from 'container'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { type CastVoteData } from 'modules/vote/api/schemas'
import { type VoteRepository } from 'modules/vote/infrastructure/repositories'
import { Vote } from 'modules/vote/domain/entities'
import { symbols } from 'modules/vote/symbols'
import { type CastVoteCommandHandler } from './castVoteCommandHandler'
describe('CastVoteCommandHandler', () => {
let castVoteCommandHandler: CastVoteCommandHandler
let voteRepository: VoteRepository
let castVoteData: CastVoteData
beforeEach(() => {
const container = ContainerSingleton.getInstance()
voteRepository = container.get(symbols.voteRepository)
castVoteCommandHandler = container.get(symbols.castVoteCommandHandler)
castVoteData = {
poolId: faker.datatype.uuid(),
voterId: faker.datatype.uuid(),
answerId: faker.datatype.uuid(),
}
})
afterEach(() => {
vi.restoreAllMocks()
})
it('should successfully cast vote', async () => {
// Arrange
const createVoteMock = vi.mocked(voteRepository.createVote)
const findVoteMock = vi.mocked(voteRepository.findVoteInPoolByVoter)
const updateVoteMock = vi.mocked(voteRepository.updateVote)
const hasVotedTwiceOnAnswerMock = vi.mocked(voteRepository.hasVotedTwiceOnAnswer)
const checkIfAnswerBelongsToPoolMock = vi.mocked(voteRepository.checkIfAnswerBelongsToPool)
// Mock vote data
const mockVoteData = {
id: faker.datatype.uuid(),
answerId: faker.datatype.uuid(),
voterId: faker.datatype.uuid(),
}
// Create mock Vote instance
const mockVote = new Vote(mockVoteData)
// Set up your mocks
findVoteMock.mockResolvedValueOnce({ vote: mockVote })
hasVotedTwiceOnAnswerMock.mockResolvedValueOnce(false)
checkIfAnswerBelongsToPoolMock.mockResolvedValueOnce(true)
// Act
await castVoteCommandHandler.execute(castVoteData)
// Assert
expect(createVoteMock).toHaveBeenCalledWith(castVoteData)
expect(updateVoteMock).not.toHaveBeenCalled()
})
})
However, the test is failing with the following error:
TypeError: findVoteMock.mockResolvedValueOnce is not a function
Five beers later, after reading the documentation carefully, I came to this solution: