alice-tester

A module for testing Yandex Alice skills

Usage no npm install needed!

<script type="module">
  import aliceTester from 'https://cdn.skypack.dev/alice-tester';
</script>

README

alice-tester

Actions Status Coverage Status Known Vulnerabilities npm license

Библиотека для автоматического тестирования навыков Алисы на Node.js. Позволяет эмулировать сообщения пользователя в соответствии с протоколом и проверять ответы навыка.

Содержание

Установка

npm i alice-tester --save-dev

Использование

Если веб-сервер с навыком запущен локально на http://localhost:3000, то тест может выглядеть так:

const assert = require('assert');
const User = require('alice-tester');

it('should get welcome message', async () => {
  const user = new User('http://localhost:3000');
  await user.enter();
  
  assert.equal(user.response.text, 'Добро пожаловать!');
});

Запустить тест можно через mocha:

$ mocha test.js

  ✓ should get welcome message

  1 passing (34ms)

Дальше можно добавить тест взаимодействия с навыком. Спросить пользователем "Что ты умеешь?" и проверить текстово-голосовой ответ и кнопки:

const assert = require('assert');
const User = require('alice-tester');

it('should show help', async () => {
  const user = new User('http://localhost:3000');
  await user.enter();
  await user.say('Что ты умеешь?');

  assert.equal(user.response.text, 'Я умею играть в города.');
  assert.equal(user.response.tts, 'Я умею играть в город+а.');
  assert.deepEqual(user.response.buttons, [{title: 'Понятно', hide: true}]);
});

Когда тестов станет больше, запуск/остановку сервера удобно вынести в before/after хуки:

const assert = require('assert');
const User = require('alice-tester');
const server = require('./server');

const PORT = 3000;

before(done => {
  server.listen(PORT, done);
});

after(done => {
  server.close(done);
});

it('should get welcome message', async () => {
  const user = new User(`http://localhost:${PORT}`);
  await user.enter();

  assert.equal(user.response.text, 'Добро пожаловать!');
});

it('should show help', async () => {
  const user = new User(`http://localhost:${PORT}`);
  await user.enter();
  await user.say('Что ты умеешь?');

  assert.equal(user.response.text, 'Я умею играть в города.');
});

Результат:

$ mocha test.js

  ✓ should get welcome message
  ✓ should show help

  2 passing (37ms)

Более подробно про разработку тестов для навыков можно почитать в статье на Хабре.

API

User.config

Глобальный конфиг класса User:

  • generateUserId {Function} - функция генерации userId. По умолчанию: () => `${Date.now()}-${Math.random()}`
  • responseTimeout {Number} - таймаут для ответа навыка (мс). По умолчанию: 1000
  • webhookUrl {String|Function|HTTP.Server} - дефолтный вебхук-урл. По умолчанию: ''
  • stopWords {Array<String|RegExp>} - список стоп-слов. По умолчанию: undefined|null|NaN|true|false

Пример:

User.config.generateUserId = () => Date.now();
User.config.responseTimeout = 500;
User.config.webhookUrl = 'http://localhost:3000';

new User([webhookUrl], [extraProps])

Создание нового пользователя для теста.
Параметры:

  • webhookUrl {?String|Function|HTTP.Server} - вебхук-урл навыка в виде строки или инстанса http.Server. Если не передан, используется дефолтный из User.config.webhookUrl.
  • extraProps {?Object|Function} - объект с полями, которые будут добавлены к каждому запросу, либо функция модификации тела запроса.

Примеры:

// обычный пользователь
const user = new User('http://localhost');

// пользователь с заданным user_id
const user = new User('http://localhost', {session: {user_id: 'custom-user-id'}});

// пользователь без экрана
const user = new User('http://localhost', body => delete body.meta.interfaces.screen);

// используем дефолтный вебхук-урл
User.config.webhookUrl = 'http://localhost:3000';
const user = new User();

user.enter([message], [extraProps])

Вход пользователя в навык.
Параметры:

  • message {?String=''} - фраза, с которой пользователь пришел в навык. При этом в original_utterance по умолчанию будет "запусти навык тест {message}".
  • extraProps {?Object|Function} - объект с полями, которые будут добавлены к телу запросу, либо функция модификации тела запроса.

Returns: Promise<response>

user.say(message, [extraProps])

Отправка сообщения в навык.
Параметры:

  • message {String} - сообщение. По умолчанию одинаковое в command и original_utterance.
  • extraProps {?Object|Function} - объект с полями, которые будут добавлены к телу запросу, либо функция модификации тела запроса.

Returns: Promise<response>

user.tap(title, [extraProps])

Нажатие пользователем на кнопку с заданным текстом. Если предыдущий запрос не вернул кнопок, то будет ошибка. Если предыдущий запрос вернул кнопки с дополнительными данными (payload), то эти данные будут прикреплены к запросу.
Параметры:

  • title {String|RegExp} - текст кнопки.
  • extraProps {?Object|Function} - объект с полями, которые будут добавлены к телу запросу, либо функция модификации тела запроса.

Returns: Promise<response>

user.tapImage(title, [extraProps])

Нажатие пользователем на изображение с заданным текстом. Если предыдущий запрос не вернул изображений-кнопок, то будет ошибка. Если предыдущий запрос вернул изображения-кнопки с дополнительными данными (payload), то эти данные будут прикреплены к запросу.
Параметры:

  • title {String|RegExp} - текст изображения-кнопки.
  • extraProps {?Object|Function} - объект с полями, которые будут добавлены к телу запросу, либо функция модификации тела запроса.

Returns: Promise<response>

user.response

Поле response из последнего ответа навыка.

user.body

Тело последнего ответа навыка.

user.id

Сгенерированный идентификатор пользователя.

user.sessionId

Сгенерированный идентификатор текущей сессии.

user.webhookUrl

Вебхук, на который шлет запросы пользователь.

user.history

История всех ответов для текущего пользователя.

Проверка времени ответа

Если время ответа на запрос превышает User.config.responseTimeout, то тест упадет с ошибкой:

Response time (1056 ms) exceeded timeout (1000 ms)

Можно выставить любое другое значение порога, либо отключить проверку значением 0.

Проверка размеров ответа

На многие поля ответа накладываются ограничения по длине. Например, response.text не может быть больше 1024 символов. В процессе прогона тестов alice-tester автоматически проверяет все ответы навыка на соответствие лимитам и кидает ошибку в случае их нарушения:

Length of response.text (1049) is greater than allowed (1024): События романа «Война и мир» происходят ... и преодолевать любые трудности.

Проверка стоп-слов

Все ответы навыка при тестировании прогоняются через фильтр стоп-слов, которые обычно сигнализируют об ошибке в навыке. Список стоп-слов по умолчанию:

undefined
null
NaN
true
false

Например, если в response.text оказалась строка "Привет, undefined", то alice-tester автоматически бросит ошибку:

Stop word "undefined" found in response.text: "Привет, undefined"

Отладка тестов

Для отладки тестов можно выставить переменную окружения DEBUG=alice-tester (см debug). Тогда в консоль будут выводится все отправляемые запросы и ответы:

DEBUG=alice-tester mocha test.js

В консоли:

alice-tester REQUEST: {"request":{"command":"","original_utterance":"","type":"SimpleUtterance"},"session":{"new":true,"user_id":"user-1","session_id":"session-1","message_id":1,"skill_id":"test-skill"},"meta":{"locale":"ru-RU","timezone":"Europe/Moscow","client_id":"ru.yandex.searchplugin/5.80 (Samsung Galaxy; Android 4.4)","interfaces":{"screen":{}}},"version":"1.0"} +0ms
alice-tester RESPONSE: {"version":"1.0","session":{"new":true,"user_id":"user-1","session_id":"session-1","message_id":1,"skill_id":"test-skill"},"response":{"text":"Это приватный навык и недоступен для публичного использования.","tts":"Это приватный навык и недоступен для публичного использования.","end_session":true}} +15ms

Лицензия

MIT @ Vitaliy Potapov