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

如何用Jest+TypeScript模拟类属性并通过CommandFactory测试?

问题:如何正确模拟Discord.js的ChatInputCommandInteraction实例进行单元测试?

我是Jest和单元测试新手,用TypeScript开发,现在要测试CommandFactory类的getCommand方法。我尝试模拟Discord.js的ChatInputCommandInteraction类,给它的commandName属性设为'add-game',但测试代码因为类型不匹配报错,提示缺少大量必需属性。

待测试类(CommandFactory.ts)

import type {ChatInputCommandInteraction} from 'discord.js';
import type CommandInterface from './interfaces/CommandInterface';
import GameCommand from './commands/GameCommand';
import SettingCommand from './commands/SettingCommand';
import SetupCommand from './commands/SetupCommand';
import commands from '@config/commands';

export default class CommandFactory {
    public getCommand(interaction: ChatInputCommandInteraction): CommandInterface {
        for (const command of commands) {
            if (command.name === interaction.commandName) {
                if (command.classType === 'SettingCommand') {
                    return new SettingCommand(interaction, command);
                }

                if (command.classType === 'GameCommand') {
                    return new GameCommand(interaction, command);
                }

                if (command.classType === 'SetupCommand') {
                    return new SetupCommand(interaction, command);
                }
            }
        }

        throw new Error(`Unexpected '${interaction.commandName}' command.`);
    }
}

测试代码(CommandFactory.test.ts)

import CommandFactory from '@components/CommandFactory';
import type {ChatInputCommandInteraction} from 'discord.js';
import type {jest} from '@jest/globals';
import {test, expect} from '@jest/globals';
import GameCommand from '@components/commands/GameCommand';

const interaction: jest.MockedObject<ChatInputCommandInteraction> = {
    commandName: 'add-game',
};

test('Instantiates GameCommand', () => {
    const factory = new CommandFactory();

    const command = factory.getCommand(interaction);

    expect(command).toBeInstanceOf(GameCommand);
});

报错信息

tests/components/commands/CommandFactory.test.ts:7:7 - error TS2322: Type '{ commandName: string; }' is not assignable to type 'MockedObject<ChatInputCommandInteraction<CacheType>>'.
      Type '{ commandName: string; }' is missing the following properties from type '{ commandType: ApplicationCommandType.ChatInput; options: MockedObject<Omit<CommandInteractionOptionResolver<CacheType>, "getMessage" | "getFocused">>; ... 50 more ...; valueOf: MockedFunction<...>; }': commandType, options, inGuild, inCachedGuild, and 46 more.

解决方案

ChatInputCommandInteraction是包含大量属性和方法的复杂接口,测试时不需要模拟所有属性,只需要提供getCommand方法用到的commandName即可。以下是几种实用的解决方式:

方法1:Partial结合类型断言

创建仅包含commandName的对象,通过类型断言告诉TypeScript它符合目标类型(测试中仅访问commandName,其他属性不会被调用):

import CommandFactory from '@components/CommandFactory';
import type {ChatInputCommandInteraction} from 'discord.js';
import {test, expect} from '@jest/globals';
import GameCommand from '@components/commands/GameCommand';

// 仅模拟需要的属性,通过双重断言绕过类型检查
const interaction = { commandName: 'add-game' } as Partial<ChatInputCommandInteraction> as ChatInputCommandInteraction;

test('Instantiates GameCommand', () => {
    const factory = new CommandFactory();
    const command = factory.getCommand(interaction);
    expect(command).toBeInstanceOf(GameCommand);
});

方法2:Mocked包裹Partial类型

如果后续需要给对象添加模拟方法,可以用jest.Mocked结合Partial,既保留类型提示,又无需模拟全部属性:

import CommandFactory from '@components/CommandFactory';
import type {ChatInputCommandInteraction} from 'discord.js';
import type {jest} from '@jest/globals';
import {test, expect} from '@jest/globals';
import GameCommand from '@components/commands/GameCommand';

// 定义仅包含部分属性的模拟类型
type MockedInteraction = jest.Mocked<Partial<ChatInputCommandInteraction>>;
const interaction: MockedInteraction = {
    commandName: 'add-game',
    // 后续需要模拟方法时可添加,比如:
    // reply: jest.fn()
};

test('Instantiates GameCommand', () => {
    const factory = new CommandFactory();
    // 断言为完整类型传入方法
    const command = factory.getCommand(interaction as ChatInputCommandInteraction);
    expect(command).toBeInstanceOf(GameCommand);
});

方法3:使用Discord.js官方测试工具(可选)

若需要更贴近真实场景的模拟,可使用Discord.js官方提供的@discordjs/testing库创建符合类型的交互对象:

import CommandFactory from '@components/CommandFactory';
import {test, expect} from '@jest/globals';
import GameCommand from '@components/commands/GameCommand';
import {createChatInputCommandInteraction} from '@discordjs/testing';

test('Instantiates GameCommand', () => {
    // 创建带指定commandName的模拟交互
    const interaction = createChatInputCommandInteraction({ commandName: 'add-game' });
    
    const factory = new CommandFactory();
    const command = factory.getCommand(interaction);
    expect(command).toBeInstanceOf(GameCommand);
});

使用前需先安装依赖:npm install @discordjs/testing --save-dev


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 06:15:39