You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jest SpyOn异常:实际方法被调用,未执行模拟实现

问题分析

你的测试失效的核心原因是:GameService内部直接硬编码实例化了GameFactory(const gameFactory = new GameFactory()),Jest对导出类的mock无法影响到函数内部新建的实例,所以实际的createNewGameWithInitialPlayer方法依然会被调用。

解决方案

解决思路是通过依赖注入将GameFactory传入GameService,而不是在内部直接创建,这样测试时可以替换为mock实例。

步骤1:重构GameService,支持依赖注入

修改GameService.ts,把GameFactory作为可选参数传入,默认值保持原有逻辑,不影响现有业务代码:

// GameService.ts
const GameService = (games: {[index: string]: Game}, gameFactory = new GameFactory()) => {
  const playerFactory = PlayerFactory()

  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)
        // ... 原有业务逻辑
    }
    // ... 原有业务逻辑
}

步骤2:修改测试代码,传入Mock实例

在测试中创建一个mock的GameFactory实例,指定createNewGameWithInitialPlayer返回undefined,然后传给GameService:

// GameService.test.ts
import { GameFactory } from '../Factories/GameFactory'
import GameService from '../GameService'

describe('Testing Game Service', () => {
    test('createRoom handles createNewGameWithInitialPlayer returning undefined', () => {
        // 构建mock的GameFactory实例
        const mockGameFactory = {
            createNewGameWithInitialPlayer: jest.fn().mockReturnValue(undefined)
        } as unknown as GameFactory

        // 传入mock实例初始化GameService
        const gameService = GameService({}, mockGameFactory)

        const response: RoomResponse = gameService.createRoom({
            name: 'Player 1',
            device: DevicesEnum.ios,
            socketID: 'some-socket-id'
        })

        // 验证mock方法是否被正确调用
        expect(mockGameFactory.createNewGameWithInitialPlayer).toHaveBeenCalled()
        // 这里添加针对response的断言,比如检查返回值是否符合预期
    })
})
补充方案(无需重构的临时写法)

如果暂时不想重构代码,也可以用Jest的mock方法直接替换整个GameFactory模块:

// GameService.test.ts
import * as GameFactoryModule from '../Factories/GameFactory'
import GameService from '../GameService'

// 提前mock整个模块
jest.mock('../Factories/GameFactory', () => {
    return {
        GameFactory: jest.fn().mockImplementation(() => {
            return {
                createNewGameWithInitialPlayer: jest.fn().mockReturnValue(undefined)
            }
        })
    }
})

describe('Testing Game Service', () => {
    test('createRoom', () => {
        const gameService = GameService({})
        const response: RoomResponse = gameService.createRoom({
            name: 'Player 1',
            device: DevicesEnum.ios,
            socketID: 'some-socket-id'
        })

        expect(GameFactoryModule.GameFactory).toHaveBeenCalled()
        expect(GameFactoryModule.GameFactory.prototype.createNewGameWithInitialPlayer).toHaveBeenCalled()
    })
})

不过这种方式耦合性较高,推荐优先使用依赖注入的方案,代码更易维护和测试。

内容的提问来源于stack exchange,提问作者lost_programmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 17:30:59