Home > Enterprise >  Jest SpyOn - Actual Method is Called, Instead of the Mocked Implementation
Jest SpyOn - Actual Method is Called, Instead of the Mocked Implementation

Time:01-28

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 })
  • Related