Перейти к основному содержимому

<mockData>

Описание

Тег <mockData> позволяет задать заглушки ответов (мок-объекты) на HTTP-запросы из сценария бота.

Когда при выполнении тестов в сценарии вызывается метод $http.query или его аналоги (например, $http.get), система проверяет, предусмотрен ли в тесте соответствующий тег <mockData> с такими же параметрами и телом, с какими выполняется HTTP-запрос.

  • Если такой тег существует и указанные в нем URL и тело запроса в точности совпадают с ожидаемыми, то система использует ответ, указанный в <mockData>, в качестве тела HTTP-ответа, полученного в результате вызова метода.

  • Если в сценарии есть вызов $http, для которого в тесте нет подходящего элемента <mockData>, HTTP-запрос будет принят за неудачный и не вернет данных в ответе. Из-за этого тест, вероятнее всего, будет провален.

Реальные HTTP-запросы при выполнении автоматических тестов не выполняются.

подсказка
Это поведение можно переопределить. Передайте с тегом <test> или <test-case> атрибут integration="true", чтобы при запуске теста или тест-кейса выполнялись HTTP-запросы. В таком случае <mockData> можно не указывать.
предупреждение
Если вы используете атрибут integration="true" и достаточно часто запускаете автоматические тесты, то реальные запросы могут быстро исчерпать квоты на использование или иметь другие нежелательные побочные эффекты во внешних системах.
Рекомендуется использовать интеграционные тесты только для тех частей сценария, где действительно необходимо проверить работу бота в связке с внешней системой, интегрированной через HTTP API.

Структура

Элемент <mockData> может иметь следующие дочерние элементы.

URL и параметры запроса

  • <query> — описание запроса. Обязательный элемент.

    • Атрибут method — HTTP-глагол, используемый при запросе. Значение по умолчанию — GET.
    • В теле элемента <query> обязательно указывается URL, на который посылается запрос.
  • <parameters> — переменные значения для подстановки в URL. Необязательный элемент.

Чтобы задать переменные значения (чаще всего это параметры запроса), можно подставить их непосредственно в тело элемента <query> или использовать отдельный элемент <parameters>.

Дочерние элементы <parameters> должны иметь вид <имя-параметра>значение-параметра</имя-параметра>, а в <query> на месте параметров должны стоять заглушки вида ${имя-параметра}.

предупреждение
В XML-тестах необходимо экранировать специальные символы: заменять & на &amp;, < на &lt;, > на &gt;.
<mockData>
<query>https://httpbin.org/get?foo=bar&amp;text=&quot;Привет, мир!&quot;</query>
<!-- ... -->
</mockData>

Тело запроса

<body> — описание тела запроса. Необязательный элемент.

Если указан тег <body>, при выполнении HTTP-запроса мок-объект дополнительно проверяется на соответствие указанного тела запроса реально переданному из сценария. В теле элемента <body> обязательно указывается JSON-объект или примитив, передача которого ожидается в теле запроса.

Для тега можно задать необязательные атрибуты:

  • strictMatch — строгость сопоставления мок-объекта телу запроса. Значение по умолчанию: false.

    • При нестрогом сопоставлении (false) проверяется, что все значения полей, указанные в теге <body>, равны соответствующим значениям полей из тела запроса.
    • При строгом сопоставлении (значение true) проверяется полное совпадение объекта в теге <body> и тела запроса.
  • field — JsonPath-выражение, указывающее на поле для сопоставления. Если атрибут указан, сопоставление производится не по всему объекту из тела запроса, а только по его части.

<mockData>
<query method="POST">https://httpbin.org/post</query>
<body>
{
"foo": "bar"
}
</body>
<!-- ... -->
</mockData>

Ответ

<response> — описание ответа, который вернет мок-объект в качестве ответа сервера. Обязательный элемент. В теле элемента указывается строка с ответом.

предупреждение
Возвращаемое значение метода $http.query помимо HTTP-ответа в поле data содержит ряд служебных полей: isOk, status и другие. Данные поля не следует помещать в <response>. Указывайте только ответ от сервера — то, что приходит в поле data.

Возможные атрибуты для тега:

  • status — код ответа на HTTP-запрос, по умолчанию 200.
  • type — тип ответа, по умолчанию json.
    • Если тип ответа json или xml, ответ парсится и возвращается в сценарий в виде JS-объекта.
    • В противном случае ответ передается в виде строки.
предупреждение
Если формат тела <response> не соответствует указанному типу, при публикации бота возникнет синтаксическая ошибка.
<mockData>
<query>https://httpbin.org/json</query>
<response>
{
"slideshow": {
"title": "Sample Slide Show"
}
}
</response>
</mockData>

Ответ в виде строки

Если указанный тип ответа не json или xml, то ответ передается в сценарий в виде строки с учетом всех пробельных символов. В следующих двух примерах ответы будут разными:

<mockData>
<query>https://httpbin.org/get</query>
<response type="text">ok</response>
</mockData>
$http.query("http://httpbin.org/get"); // => {..., data: "ok"}

Пустой ответ

Тело элемента <response> может быть пустым. В таком случае в качестве ответа в сценарий передается пустой объект.

подсказка
Пустой ответ можно использовать, например, для эмуляции ответа 204 No Content, когда сервер возвращает только заголовки без тела ответа.
<mockData>
<query>https://httpbin.org/status/204</query>
<response status="204"/>
</mockData>

Часто задаваемые вопросы

В какой элемент должны быть вложены мок-объекты?

Элементы с тегом <mockData> могут быть вложены в элемент <test> или <test-case>.

  • Мок-объекты в <test> доступны всем тест-кейсам в рамках данного теста. Их полезно использовать, когда тест-кейсы проверяют общую между собой функциональность.
  • Мок-объекты, вложенные в <test-case>, используются только при прохождении данного тест-кейса и недоступны остальным тест-кейсам.

В каком порядке нужно располагать мок-объекты?

  • Если мок-объект вложен в <test>, его можно разместить где угодно: до или после тест-кейса, где он используется, а также между тест-кейсами.
  • Если же мок-объект вложен в <test-case>, он должен быть расположен выше тега <q>, эмулирующего запрос клиента, в результате которого в сценарии выполняется HTTP-запрос.

Сколько должно быть мок-объектов на один тест-кейс?

В общем случае на один тест-кейс должно быть написано столько же мок-объектов, сколько HTTP-запросов выполняется при его прохождении.

подсказка
Исключение составляют тест-кейсы, в которых выполняется несколько одинаковых HTTP-запросов. Если все такие запросы предполагают одинаковые ответы, для них достаточно одного мок-объекта.

Можно ли проверить разные ответы на одинаковые запросы?

В одном тест-кейсе можно проверить выполнение одинаковых HTTP-запросов с разными ответами. При этом на каждый тег <q> должен быть написан свой мок-объект.

Если одному тегу <q> предшествуют несколько подходящих тегов <mockData>, то будет использован ответ из последнего тега, а все остальные будут проигнорированы.

Рассмотрим пример простого стейта и тест-кейса:

state: ExternalCall
q!: test
script:
$temp.res = $http.get("https://httpbin.org/get");
a: {{$temp.res.data}}
<test-case>
<!-- Этот мок-объект не используется ни для одного запроса -->
<mockData>
<query>https://httpbin.org/get</query>
<response type="text">1</response>
</mockData>
<!-- Этот мок-объект используется в обоих запросах ниже -->
<mockData>
<query>https://httpbin.org/get</query>
<response type="text">2</response>
</mockData>

<q>test</q>
<a>2</a>
<q>test</q>
<a>2</a>

<!-- Этот мок-объект игнорируется при запросах выше, но используется при запросе ниже -->
<mockData>
<query>https://httpbin.org/get</query>
<response type="text">3</response>
</mockData>

<q>test</q>
<a>3</a>
</test-case>

Примеры использования

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

Следующий пример взят из пошагового сценария бота с обращением к API. В нем использован мок-объект для проверки стейта, где бот передает прогноз погоды.

<test>
<mockData>
<query>http://api.openweathermap.org/data/2.5/weather?appid=${appid}&amp;units=${units}&amp;lang=${lang}&amp;q=${q}</query>
<parameters>
<appid>***</appid> <!-- Замените звездочки на свой API-ключ -->
<units>metric</units>
<lang>ru</lang>
<q>москва</q>
</parameters>
<response>
{
"weather": [
{
"main": "Rain",
"description": "небольшой дождь"
}
],
"main": {
"temp": 20.33
}
}
</response>
</mockData>

<test-case>
<q>/start</q>
<a>Привет! Я электронный помощник. Я могу сообщить вам текущую погоду в любом городе. Напишите город.</a>
<q>Москва</q>
<a>Сегодня в городе Москва небольшой дождь, 20 °C.</a>
<a>Советую захватить с собой зонтик!</a>
</test-case>
</test>

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

Пример ниже иллюстрирует различные варианты того, как тело запроса может сопоставляться с содержимым тега <body>.

state: ExternalCall
q!: test
script:
$temp.res = $http.post("https://httpbin.org/post", {
body: {
bodyParam1: "text1",
bodyParam2: "text2",
bodyObject: {
bodyParam3: 100,
bodyParam4: false
}
}
});
a: {{$temp.res.data}}
<test>
<!-- Ожидаемое тело запроса полностью совпадает с фактическим. -->
<test-case>
<mockData>
<query method="POST">https://httpbin.org/post</query>
<body strictMatch="true">
{
"bodyParam1": "text1",
"bodyParam2": "text2",
"bodyObject": {
"bodyParam3": 100,
"bodyParam4": false
}
}
</body>
<response type="text">ok 1</response>
</mockData>
<q>test</q>
<a>ok 1</a>
</test-case>

<!-- Значения проверяемых полей в теле запроса совпадают с фактическими. -->
<!-- Поскольку атрибут `strictMatch` не задан, совпадения по всем полям не требуется. -->
<test-case>
<mockData>
<query method="POST">https://httpbin.org/post</query>
<body>
{
"bodyParam1": "text1",
"bodyParam2": "text2"
}
</body>
<response type="text">ok 2</response>
</mockData>
<q>test</q>
<a>ok 2</a>
</test-case>

<!-- Ожидаемое и фактическое тело запроса сопоставляются по полю `bodyObject`. -->
<test-case>
<mockData>
<query method="POST">https://httpbin.org/post</query>
<body field="bodyObject">
{
"bodyParam3": 100,
"bodyParam4": false
}
</body>
<response type="text">ok 3</response>
</mockData>
<q>test</q>
<a>ok 3</a>
</test-case>

<!-- Здесь последний мок-объект требует полного совпадения по полю `bodyObject`. -->
<!-- Тело запроса не удовлетворяет этому условию, поэтому этот мок-объект игнорируется. -->
<!-- Предыдущий мок-объект не требует полного совпадения и подходит. -->
<test-case>
<mockData>
<query method="POST">https://httpbin.org/post</query>
<body field="bodyObject">
{
"bodyParam3": 100
}
</body>
<response type="text">ok 4</response>
</mockData>

<mockData>
<query method="POST">https://httpbin.org/post</query>
<body field="bodyObject" strictMatch="true">
{
"bodyParam3": 100
}
</body>
<response type="text">would not match</response>
</mockData>
<q>test</q>
<a>ok 4</a>
</test-case>

<!-- Если тег `<body>` не указан, сопоставление идет только по URL и методу запроса. -->
<test-case>
<mockData>
<query method="POST">https://httpbin.org/post</query>
<response type="text">ok 5</response>
</mockData>
<q>test</q>
<a>ok 5</a>
</test-case>

<!-- Если значение поля — JSON-примитив, по нему также можно проводить сопоставление. -->
<test-case>
<mockData>
<query method="POST">https://httpbin.org/post</query>
<body field="bodyParam2">"text2"</body>
<response type="text">ok 6</response>
</mockData>
<q>test</q>
<a>ok 6</a>
</test-case>
</test>