Skip to content

Error Simulation

failNext — one-shot error

Forces the next call to a specific API method to reject with a GrammyError. After firing once, subsequent calls revert to normal.

ts
import { GrammyError, prepareBot } from 'grammy-testing';

const { chats } = await prepareBot(createBot());
const user = chats.newUser();

// Tell the transformer: the next sendMessage should throw 403
chats.outgoing.failNext('sendMessage', {
  code: 403,
  description: 'Forbidden: bot was blocked by the user',
});

// Bot tries to send a message, gets a GrammyError
await user.sendText('trigger');
// The bot's error handler should run now

// Recovery: the next call goes through normally
await user.sendText('again');
expect(user.replies.lastOrThrow().text).toBe('...');

failAll — sticky error

Forces every call to a method to fail until clearOverrides() is called.

ts
chats.outgoing.failAll('getChatMember', {
  code: 400,
  description: 'Bad Request: chat not found',
});

// Multiple dispatches — all getChatMember calls fail
await user.sendCommand('/check');
await user.sendCommand('/verify');

// Remove the sticky failure
chats.outgoing.clearOverrides();

// Now succeeds
await user.sendCommand('/check');

Testing your bot's error handler

ts
import { GrammyError } from 'grammy'; // also re-exported from grammy-testing

const bot = new Bot('token');

bot.catch(async (err) => {
  if (err.error instanceof GrammyError && err.error.error_code === 403) {
    // handle blocked user
  }
});

bot.command('notify', async (ctx) => {
  await ctx.reply('Notification sent!');
});

const { chats } = await prepareBot(bot);
const user = chats.newUser();

chats.outgoing.failNext('sendMessage', { code: 403, description: 'Forbidden' });
await user.sendCommand('/notify');

// Assert the error path ran (e.g. via a flag set in the error handler)
expect(errorHandlerRan).toBe(true);

Combining failNext with respondNext

ts
// Make the first call succeed with a specific value:
chats.outgoing.respondNext('getChatMember', { status: 'kicked' });

// Make the second call fail:
chats.outgoing.failNext('getChatMember', { code: 403, description: 'Forbidden' });

GrammyErrorSpec

Both failNext and failAll accept either a real GrammyError or a shorthand spec:

ts
// Shorthand:
{ code: 403, description: 'Forbidden: ...' }

// Real GrammyError (e.g. to set extra fields):
import { GrammyError } from 'grammy-testing';
const err = new GrammyError('...', { error_code: 403, description: '...', ok: false }, 'sendMessage', {});
chats.outgoing.failNext('sendMessage', err);

Released under the MIT License.