Эта статья является четвертой (и последней!) частью серии статей (№1, №2, №3, №4) о том, как мы настраиваем чат-бота - NinjaChat. - для обслуживания наших грузоотправителей и грузополучателей здесь, в Ninja Van. В этом последнем разделе вкратце рассказывается о настройке тестирования нашего чат-бота и новых функциях, находящихся в разработке для NinjaChat.

Краткое замечание по тестированию нашей интеграции

Создание чего-то полностью с нуля означает, что нам также нужны гарантии того, что оно будет работать должным образом, когда мы, наконец, развернем. Это особенно верно для приложения, основанного на дереве разговоров, где мы должны стремиться охватить как можно больше возможных путей. Таким образом, нам нужна была хорошая основа для тестирования приложения на разных уровнях.

  • Модульное тестирование

Одно из преимуществ написания наших классов обслуживания намерений таким образом, чтобы каждое совпадающее намерение несло значение действия, которое выполняет фиксированный метод обработчика, состоит в том, что запись модулей для них включает в себя тестирование только метода обработчика и ожидание данного вывода в форме DialogflowQueryResponseBean. Вот пример, который проверяет реакцию на ошибку, когда одна из наших внутренних служб выдает исключение:

@Test
public void givenNoOrders_whenHandleActionY_thenProcessAction() {
    //Arranging the error from an internal api call
    when(orderApi.searchForOrders(/* ... */))
        .thenReturn(completedFuture(Optional.empty()));
//Asserting on the response as an error bean
    assertErrorBean(intentService.processMatchedIntent(
        buildCustomerRequest(DEFAULT_SYSTEM_ID),
        buildQueryResult(CUSTOMER_FETCH_ORDERS_FOR_ISSUES_SEARCH)),
        DF_FETCH_ORDERS_FAILURE.getKey()));
}

* Примечание: buildQueryResult() имитирует совпадающий результат намерения из Dialogflow с заданным действием CUSTOMER_FETCH_ORDERS_FOR_ISSUES_SEARCH. DF_FETCH_ORDERS_FAILURE относится к ключу сообщения об ошибке, показываемому пользователю.

  • Тестирование интеграции Dialogflow

Естественно, как только мы убедились, что отдельные обработчики для каждого совпадающего действия намерения ведут себя так, как ожидалось (т.е. возвращают правильный ответ пользователю, делают правильные вызовы API и соответствующим образом анализируют ответы от промежуточных вызовов) с помощью модульных тестов, мы также используем внутренняя структура, которая позволяет нам тестировать работу чат-бота с более интегрированной, актуальной для пользователя точки зрения.

Это означает, что мы запускаем интеграционные тесты, которые тестируются на уровне основного класса обслуживания Dialogflow. Этот класс отвечает за прием строки запроса от данного пользователя, преобразование ее в bean-компонент запроса Dialogflow, его обработку и формирование bean-компонента ответа Dialogflow и, наконец, возврат ответа в соответствии с тем, что пользователь ожидает увидеть на любой данной платформе.

Вот пример одного из таких тестов:

@Test
public void givenTrackOrder_whenUserChatsWithBot_thenProcessConversation() {
    AbstractModule customModule = configureCustomerProfileModule(
        new DialogflowCustomerIntegrationTestProfile()
            .buildDefaultProfile());
DialogflowConversationContainer con = 
        newConversation(customModule, CUSTOMER);
con.assertThat(whenUserSays("hello there")
        .thenBotRepliesWith("Thank you for using Ninja Van!" 
            + "How can I help you today?")
        .andShowsOptions("Track Orders", 
            "Reschedule Delivery", "Other Options"));
con.assertThat(whenUserSays("i want to track my order")
        .thenBotRepliesWith("Here is a list of your orders...")
        .andShowsOptions("NVSGD123456", 
            "NVSGD135790", "NVSGD246810", "Return to Main Menu"));
/* ... and so on */
}

Этот тест охватывает серию взаимодействий, начиная с того, как пользователь здоровается с нашим ботом, отслеживает их порядок и, наконец, возвращается в главное меню.

Что следует отметить:

  • Мы создали класс DialogflowCustomerIntegrationTestProfile, который инициализируется для каждого теста с подробной информацией о клиенте, включая количество заказов, которые у него есть, их данные, адреса и т. Д. Эти данные затем используются в имитируемых ответах, возвращаемых из внешних вызовов API.
  • DialogflowConversationContainer - это контейнер для этого класса обслуживания, который абстрагируется от настройки класса обслуживания и включает в себя установку переменных среды, внедрение зависимостей в тестируемую службу и т. Д. Через настраиваемый модуль.
  • Контейнер также имеет вспомогательные методы, которые позволяют нам обрабатывать утверждения, сгенерированные whenUserSays(…).thenBotRepliesWith(…).andShowsOptions(…), который возвращает объект DialogflowConversationTurn. Под обработкой утверждений я подразумеваю создание bean-объекта запроса, обращение к фактическому URL-адресу Dialogflow для обнаружения совпадений намерений с этим запросом и утверждение в проанализированном ответе.
  • Сквозное тестирование

Наконец, мы привлекли нашу команду QA к написанию сквозных автоматических тестов через фиктивную конечную точку, которая выполняет фактические вызовы другим внутренним службам и возвращает фактический HTTP-запрос, который отправляется на данную платформу в качестве ответа. Таким образом, мы можем гарантировать, что поведение нашего чат-бота, работающего в зависимости от других сервисов, будет более надежно покрыто.

Проще говоря, эти тесты включали запуск запросов, содержащих строковый запрос и заданную платформу (например, Messenger), и ожидание того, что ответ будет соответствовать полезной нагрузке, которую SNS отправляет в Messenger.

* О, в качестве интересного примечания, мы также добавили намерение, которое соответствует секретному коду, который при передаче боту экспортирует агент, настроенный для данной среды (например, local, qa и т. д.), и отображает такие статистические данные, как количество намерений, отсутствие обучающих фраз на разных языках, среднее количество обучающих фраз, предоставляемых для каждого языка для каждого намерения, и т. д. Однако фактическая полезность этой функции на данный момент все еще не определена.

Что ждет нас в будущем

Забегая вперед, наша команда планирует использовать Dialogflow как мост между продуктом и технологией, как пространство, где и продукт, и технология могут определять требования и структуру новых и измененных потоков.

Учитывая, что Dialogflow уже имеет простой в использовании и интуитивно понятный (по большей части) пользовательский интерфейс в своей консоли, это, возможно, не имеет значения, отмечая, что, немного привыкнув, дизайнеры продукта могут свободно работать над новыми прототипами, создавая эти намерения напрямую. . Это будет включать рабочий процесс, который вращается вокруг них, приближаются к технологиям только тогда, когда новые действия, параметры или ключи сообщений необходимо обрабатывать на бэкэнде.

Фактически, практически не требуется усилий при разработке, чтобы просто вставить новое намерение, которое повторно использует существующее действие, скажем CONFIRM_SELECTION, между двумя существующими намерениями. Социальная сеть продолжит обработку нового согласованного намерения, блаженно не подозревая об этом дополнительном шаге в нашем потоке. А развертывание в различных средах включает просто обновление указанного агента в других средах.

В настоящее время мы уже экспериментируем с шагами для облегчения этой передачи (или, по крайней мере, совместного использования) права собственности на Dialogflow с дизайнерами продукта, позволяя настраивать отображение вариантов ответа через само намерение. Добавив тип отображения в конец значения действия для намерения, он переопределит тип отображения, установленный для ответа на серверной части.

Например, любое намерение с действием CONFIRM_SELECTION?MENU будет отображать варианты «Да» / «Нет» как часть ответа пользователям Messenger в виде кнопок меню (вертикальных). Любое намерение с действием CONFIRM_SELECTION?FIXED покажет те же варианты, что и быстрые ответы.

Заключение

Конечно, это начало того, что мы изучаем как возможные способы гибкого управления ботом с помощью Dialogflow.

За возможными предложениями о том, как можно более эффективно использовать Dialogflow (с учетом того, что вы читали до сих пор), не стесняйтесь обращаться к любому члену нашей команды - ко мне, Амиру Ариффину или Мани Курамбойиной. . По общему признанию, то, что мы затронули, еще не приблизилось к использованию полного набора функций, доступных в Dialogflow, и эта первоначальная интеграция будет продолжать пересматриваться и улучшаться по мере того, как мы расширяем и вводим новые функции в бота.

Я также хотел бы поблагодарить следующих людей за их вклад в этот документ, наше путешествие с Dialogflow и, конечно же, за общее наставничество и руководство: Амир, Мани, Шон, Эдисон Мок (Google) и Хан Вен Кам (Google).

Спасибо за прочтение!