Как тестировать навык Алисы без публикации: BotTest и Jest

Локальное тестирование навыка Яндекс.Алисы без публикации: диалог в консоли через BotTest, автотесты на Jest с сохранением состояния и проверка вебхука.

Как тестировать навык Алисы без публикации: BotTest и Jest

26 Сентября, 2026 Автор: Максим М

Как тестировать навык Алисы без публикации: BotTest и Jest

Разбираемся, как отлаживать навык для Яндекс.Алисы локально — в консоли и в автотестах — до публикации в каталог.

Классический цикл отладки навыка выглядит так: поправил код → задеплоил на сервер → открыл тестовый диалог в Яндекс.Диалогах → понял, что не так → повторил. Каждый круг занимает минуты и требует сервера с HTTPS. Между тем почти всё, что нужно проверить в навыке, — это логика: на какую фразу что ответить, что сохранится в состоянии пользователя, уложится ли текст в лимит 1024 символа. Всё это гоняется локально, без публикации и даже без HTTP-сервера — через класс BotTest и Jest. Разберём оба уровня и границу, где всё же нужен настоящий вебхук.

BotTest: диалог с навыком в консоли

BotTest — это тот же код приложения, но вместо вебхука — интерактивный режим в терминале: вы вводите фразы пользователя, получаете ответы навыка. Из документации по тестированию:

import { BotTest } from 'umbot/test';
import { voicePlatforms } from 'umbot/plugins';

const bot = new BotTest();
bot.use(voicePlatforms); // Алиса, Маруся, SmartApp
// ...команды навыка...
await bot.test({
    isShowResult: true, // полный JSON ответа платформы
    isShowStorage: true, // содержимое userData и state
    isShowTime: true, // время обработки запроса
});

Три флага закрывают главные вопросы отладки. isShowResult показывает, что реально уйдёт платформе: видно и превышение лимита текста, и структуру кнопок. isShowStorage — что навык запомнил о пользователе: удобно ловить состояния, которые «залипают» между шагами. isShowTime — не съедает ли обработка бюджет ответа (у Алисы практический ориентир — 3 секунды). Для выхода введите exit. Нужна конкретная платформа — передайте её в конструктор: new BotTest('alisa') или new BotTest('telegram').

Jest: автотесты на логику навыка

Когда логика обрастает шагами и состояниями, консоли уже мало — нужны повторяемые проверки, которые не зависят от внимательности тестировщика. Тот же BotTest используется в Jest: метод run() принимает payload платформы и возвращает сформированный ответ:

import { BotTest } from 'umbot/test';
import { voicePlatforms, T_ALISA } from 'umbot/plugins';
import { MyController } from '../src/MyController';

describe('MyController', () => {
    let bot: BotTest;

    beforeAll(() => {
        bot = new BotTest();
        bot.use(voicePlatforms);
        bot.setAppConfig({ isLocalStorage: true });
        bot.initBotController(MyController);
    });

    it('приветствует нового пользователя', async () => {
        const result = await bot.run(
            T_ALISA,
            JSON.stringify({
                version: '1.0',
                session: { message_id: 0, user_id: 'test-user' },
                request: { command: 'привет', original_utterance: 'Привет' },
            }),
        );
        expect(JSON.stringify(result)).toContain('Привет');
    });
});

Писать JSON запроса руками не обязательно: метод simulate() сам собирает корректный payload для указанной платформы. Для проверки многошаговых сценариев учтите одну деталь: у голосовых платформ состояние пользователя путешествует внутри запроса и ответа, поэтому состояние из ответа нужно передать в следующий вызов — ровно так, как это делает Алиса:

import { BotTest } from 'umbot/test';
import { AlisaAdapter, T_ALISA } from 'umbot/plugins';

it('засчитывает правильный ответ в игре', async () => {
    const bot = new BotTest();
    bot.use(new AlisaAdapter());
    bot.setAppConfig({ isLocalStorage: true });

    bot.addCommand('game_start', ['играть'], (_, bc) => {
        bc.userData.score = 0;
        bc.text = 'Сколько будет 2+2?';
        bc.thisIntentName = 'game_answer';
    });
    bot.addStep('game_answer', (bc) => {
        if (bc.userCommand === '4') {
            bc.userData.score = Number(bc.userData.score ?? 0) + 1;
        }
        bc.text = `Счёт: ${bc.userData.score}`;
        bc.thisIntentName = null;
    });

    const first: any = await bot.simulate('играть', { platform: T_ALISA, count: 1 });
    expect(first.response.text).toBe('Сколько будет 2+2?');

    // передаём состояние из ответа в следующий запрос
    const second: any = await bot.simulate('4', {
        platform: T_ALISA,
        count: 2,
        state: first.session_state,
    });
    expect(second.response.text).toBe('Счёт: 1');
});

Для чат-платформ (Telegram, VK, MAX) в симуляции отправка в API платформы пропускается, поэтому тесты работают без токенов и сети; текст ответа в этом случае проверяется через контроллер, а не через результат вызова.

Что стоит покрыть тестами в первую очередь

  • Запуск навыка. Первый запрос сессии с пустой командой должен давать приветствие, а не «не поняла» — именно это первым проверяет модерация.
  • Помощь и выход. Фразы «помощь», «что ты умеешь», «хватит» — обязательный минимум любого навыка.
  • Нераспознанные фразы. Случайный текст должен вести к понятному ответу с подсказкой, что делать дальше.
  • Каждая ветка многошагового сценария. Особенно выход из шага: если его забыть, пользователь «застревает» в одном вопросе.
  • Длинные ответы. Тексты из базы или внешнего API могут внезапно превысить лимит в 1024 символа.

Что проверять локально, а что — только на вебхуке

Локально закрывается большая часть отладки: точность распознавания команд (те же фразы, что шлёт платформа), состояние пользователя, лимиты текста и TTS, структура кнопок и карточек, время обработки. Не проверяется то, что живёт между платформой и вашим сервером: реальный распознанный текст голосом вместо печатного, поведение именно Яндекс.Диалогов, авторизация внешних сервисов. Для этого поднимите туннель (ngrok http 3000 или cloudflared), укажите выданный HTTPS-URL в консоли разработчика — и гоняйте навык в настоящем тестовом диалоге до публикации. Но это последний этап, а не каждый шаг: основную логику дешевле закрывать локально.

Куда дальше

Рекомендую к прочтению следующие статьи:

Как перенести бота из Telegram в MAX: пошаговый план

Как перенести бота из Telegram в MAX: пошаговый план

Перенос чат-бота из Telegram в мессенджер MAX: регистрация через business.max.ru, соответствие API, вебхук, лимиты и как не поддерживать два разных проекта.

Читать статью

Бот для ВКонтакте на Node.js: Callback API и пример на TypeScript

Бот для ВКонтакте на Node.js: Callback API и пример на TypeScript

Как создать чат-бота для сообщества ВКонтакте на Node.js: настройка Callback API, подтверждение сервера, секретный ключ, клавиатура и хранение данных.

Читать статью

Как навык Алисы запоминает пользователя: state и база данных

Как навык Алисы запоминает пользователя: state и база данных

Где хранить данные пользователя в навыке Алисы: session_state, application_state и user_state, лимит 1 КБ, частые ошибки и когда нужна своя база данных.

Читать статью

Визуальный конструктор чат-ботов с экспортом в код — Umbot Flow

Визуальный конструктор чат-ботов с экспортом в код — Umbot Flow

Бесплатный визуальный конструктор чат-ботов и навыков Алисы: сценарий на холсте, симулятор и экспорт в TypeScript-проект для Telegram, VK, MAX и Алисы.

Читать статью

Вебхук для Telegram-бота на Node.js: HTTPS, nginx и secret_token

Вебхук для Telegram-бота на Node.js: HTTPS, nginx и secret_token

Как настроить вебхук Telegram-бота на Node.js: требования к HTTPS и портам, nginx, secret_token, setWebhook, локальная отладка и почему бот не отвечает.

Читать статью

Комментарии

Оставить комментарий

Как со мной связаться?

Свяжитесь со мной по любому поводу!
Я с радостью отвечу на все вопросы!

Телефон:

+7(909) 281 35-20

Дополнительная почта:

info@maxim-m.ru

Я в социальных сетях:

ВверхВверх 👆