I've searched high and low to try and get my Jest test to work but have gotten nowhere in my search.
I've got a function (createRoom
) that I want to unit test and I want to mock a method (gameFactory.createNewGameWithInitialPlayer()
) call.
GameService.ts
const GameService = (games: {[index: string]: Game}) => {
const playerFactory = PlayerFactory()
const gameFactory = new GameFactory()
const createRoom = ({name, device, socketID}: {name: string, device: string, socketID: string}): RoomResponse => {
const player = playerFactory.createNewPlayer(name, device, socketID)
if (player && player.id) {
const game: Game|undefined = gameFactory.createNewGameWithInitialPlayer(player)
...
}
...
}
GameFactory.ts
export class GameFactory {
createNewGameWithInitialPlayer = (player: Player): Game|undefined => {
const game = new Game()
game.spectators[player.id as any as number] = player
return game
}
}
GameService.test.ts
import * as gameFactory from '../Factories/GameFactory'
describe('Testing Game Service', () => {
test('createRoom', () => {
jest.spyOn(gameFactory, 'GameFactory').mockReturnValue({ createNewGameWithInitialPlayer: jest.fn().mockReturnValue(undefined)})
const response: RoomResponse = gameService.createRoom({
name: 'Player 1',
device: DevicesEnum.ios,
socketID: 'some-socket-id'
})
...
}
...
}
In GameService.test.ts
I am mocking the return value of the createNewGameWithInitialPlayer
method call. However, when I run the test, the actual implementation of it runs, instead of my mocked version. For this test in particular, I want the createNewGameWithInitialPlayer
method to return undefined
, but that does not happen, it appears to be calling the actual method implementation.
CodePudding user response:
If you want to override the createNewGameWithInitialPlayer and return what you want then, you have to mock the import of GameFactory class in your test.
// Here you are creating your mock and saying the default return Game object
const mockCreateNewGameWithInitialPlayer = jest.fn().mockImplementation(() => new Game());
// Here you say to jest that any file who wants to import "GameFactory"
// will import this fake class
jest.mock('rootofGameFactory/GameFactory', () => ({
GameFactory: function(){
return {
// and when any file wants to execute this method, will execute my mock
createNewGameWithInitialPlayer: mockCreateNewGameWithInitialPlayer
}
}
}))
describe('Testing Game Service', () => {
test('createRoom', () => {
const response: RoomResponse = gameService.createRoom({
name: 'Player 1',
device: DevicesEnum.ios,
socketID: 'some-socket-id'
})
...
}
...
}
If you want to change the return object of your mocked method, you have to do it like this...
test('createRoom 2', () => {
//Here you say to jest, just ONCE (for this test) return an instance of Game2
mockCreateNewGameWithInitialPlayer.mockImplementationOnce(() => new Game2())
const response: RoomResponse = gameService.createRoom({
name: 'Player 1',
device: DevicesEnum.ios,
socketID: 'some-socket-id'
})
...
}
CodePudding user response:
The jest documentation mentions
By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries. If you want to overwrite the original function, you can use jest.spyOn(object, methodName).mockImplementation(() => customImplementation) or jest.replaceProperty(object, methodName, jest.fn(() => customImplementation));
So you could do something like
jest.spyOn(gameFactory, 'GameFactory').mockImplementation(() => { return undefined })