E2E Testing
TestingDjsAdapter replaces the real discord.js client with a fake one, letting you run the full pipeline (DI, interceptors, and handler) without a bot token or a live Discord connection.
Setup
import { NodecordClient } from '@nodecord/core';
import { TestingDjsAdapter, createMockChatInputInteraction } from '@nodecord/djs-adapter/testing';
import { BotModule } from './bot.module';
describe('UtilModule', () => {
let adapter: TestingDjsAdapter;
beforeEach(() => {
adapter = new TestingDjsAdapter();
NodecordClient.create({
module: BotModule,
adapter,
options: { logger: false },
});
});
});
Simulating interactions
createMockChatInputInteraction accepts an object with the interaction data:
const interaction = createMockChatInputInteraction({
commandName: 'ping',
user: { id: '1', username: 'juan', discriminator: '0000' } as any,
guild: null, // null for DM context
});
await adapter.simulateInteraction(interaction);
expect(interaction.deferReply).toHaveBeenCalledOnce();
expect(interaction.editReply).toHaveBeenCalledWith('Pong! Hello, juan!');
All properties on interaction (deferReply, editReply, reply, etc.) are vitest mock functions.
Commands with @DeferReply()
When a command uses @DeferReply(), the framework calls deferReply() before executing the handler and editReply() with the response, not reply(). Assert accordingly:
expect(interaction.deferReply).toHaveBeenCalledOnce();
expect(interaction.editReply).toHaveBeenCalledWith({ content: 'Bot status: online' });
Async commands with fake timers
If a command has internal async delays, simulateInteraction will hang waiting for them. Use vitest's fake timers:
it('defers and replies', async () => {
vi.useFakeTimers();
const interaction = createMockChatInputInteraction({ commandName: 'ping', user: mockedUser });
const simulation = adapter.simulateInteraction(interaction);
await vi.runAllTimersAsync();
await simulation;
expect(interaction.editReply).toHaveBeenCalledWith('Pong! Hello, juan!');
});
Testing guild vs DM context
Pass guild to simulate a server context, or null for DMs:
// Inside a guild
const interaction = createMockChatInputInteraction({
commandName: 'server-info',
guild: { name: 'Test Server', memberCount: 42, createdTimestamp: 0 } as any,
user: mockedUser,
});
// In a DM
const interaction = createMockChatInputInteraction({
commandName: 'server-info',
guild: null,
user: mockedUser,
});