Бывали ли вы когда-нибудь на демо, на котором разработчики с гордостью показывали экран за экраном JSON вывод API, а пользователи путались и отвлекались, будучи не в силах разобраться в этом? Вы когда-нибудь пытались использовать API в разработке и были разочарованы тем, как трудно найти правильную полезную нагрузку JSON и заголовки, чтобы протестировать фичу? Demo Front-End — это простой пользовательский интерфейс, предлагающий базовые возможности для демонстрации и ознакомления с таким API.

Мотивация

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

Но что, если продукт представляет собой API? Обычно мы рекомендуем, чтобы бэкенд и фронтенд разрабатывались одной командой, потому что обычно такой подход приводит к более высокому качеству и сокращению времени разработки по сравнению с той ситуацией, когда приходится координировать работу двух отдельных команд. Однако бывают случаи, когда это невозможно: иногда бэкенд (API) разрабатывается компанией, которая продаёт третьим лицам доступ к полезному сервису через этот API. В качестве примера можно привести финансовое учреждение, предоставляющее API «платёжного шлюза», который позволяет сайтам онлайн-магазинов принимать платежи от клиентов; или поставщика услуг, который через API взаимодействует с системами сравнения цен.

Во всех тех случаях, когда API не имеет естественного пользовательского интерфейса, становится трудно сделать содержательную демонстрацию. Иногда команда пытается продемонстрировать использование API, показывая JSON-код, возвращаемый API, но это бывает нелегко понять, особенно стейкхолдерам без технического опыта. А дать возможность заинтересованным лицам повзаимодействовать с продуктом становится практически невозможно.

Я считаю, что в этом отношении большинство моих потребностей удовлетворяет jQuery; он хоть и немодный, но мощный и лаконичный. Современной альтернативой может стать React (без сборки), а современный JavaScript настолько мощный, что его можно использовать напрямую, без библиотек и фреймворков.

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

Преимущества такого демо не ограничиваются показом программного обеспечения во время демонстраций. Как только вы сделаете его доступным, он будет использоваться разработчиками для тестирования новых фич на своих локальных машинах перед отправкой кода в репозиторий, а также аналитиками качества, владельцами продуктов и другими заинтересованными лицами для тестирования продукта в тестовых средах. Также его можно использовать для демонстрации использования API потенциальным партнерам, которые могут быть заинтересованы в приобретении доступа к нему. Demo Front-End — это подарок, который не перестает быть подарком.

Практические советы

Demo Front-End лучше всего работает, когда он сразу доступен во всех местах, где есть соответствующий API. Например, в приложении Spring Boot можно разместить статические HTML, CSS и JavaScript активы в папке src/main/resources/public/testdrive, чтобы к ним можно было получить доступ, открыв браузер, например, по адресу https://localhost:8080/testdrive/. Простейший демонстрационный пользовательский интерфейс — это немного больше, чем простая замена Postman:

A screenshot of the simplest possible demo UI,          showing an input text area with an editable input JSON, and an output text area with the          response JSON from the API. The output text area has a green background to signify a successful         response
Рисунок 2: Пользователь может настроить полезную нагрузку запроса, метод и путь: ответ появляется в нижнем окне, окрашенном в зеленый цвет, что означает успешный ответ.

Если вы используете Spring Boot, обязательно установите devtools в свой проект, чтобы можно было тестировать изменения статических ассетов, таких как демо фронтенд, без необходимости перезапускать сервер.

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

$(document).ready(() => {
  $.get("test-data/hello.json", (data) =>
    $("#hello-input").val(data));
});
A screenshot of the same UI, showing an error         response colored in pink, because of a missing parameter
Рисунок 3: Ответы при ошибках становятся более наглядными благодаря окрашиванию области выводимого текста в розовый цвет

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

Несмотря на то что на данном этапе мы всё ещё показываем JSON как на входе, так и на выходе, у нас есть значительное преимущество перед Postman — поскольку мы можем использовать автоматизацию для дополнения или изменения статической версии входного JSON, предлагаемой пользователю. Если, например, правильный запрос должен содержать уникальный идентификатор, короткий сниппет JavaScript-кода может сгенерировать случайный идентификатор без каких-либо усилий со стороны пользователя. Главное здесь то, что пользовательский интерфейс позволяет провести быструю проверку с минимальным замедлением.

Для создания подобного демонстрационного фронтэнда необходим минимальный JavaScript: современный JS достаточно мощный и не требует специальных библиотек; хотя разработчикам может быть удобно использовать лёгкие инструменты, такие как htmx, jQuery или даже встроенный React. Мы рекомендуем не создавать специальную сборку, так как это влечёт за собой дополнительные шаги между запуском API и выполнением теста через пользовательский интерфейс. В идеале единственная сборка, которую мы хотели бы запустить, — это сборка самого API-продукта. Любая задержка между желанием протестировать что-то и моментом, когда мы на самом деле выполняем тест, замедляет цикл разработки.

Вызов API с помощью обращения к серверу:

$(document).ready(() => {
  $("#hello-button").click(sayHelloRequest);
});

function sayHelloRequest() {
  $("#hello-output").val("");
  $("#hello-spinner").show()
  $.ajax({
    method: $("#hello-method").val(),
    data: $("#hello-input").val(),
    contentType: "application/json",
    url: $("#hello-path").val(),
    success: onSayHelloSuccess,
    error: onSayHelloError,
    complete: onSayHelloComplete,
  });
}

function onSayHelloSuccess(content) {
  $("#hello-output").
    removeClass("error").
    addClass("success").
    val(indent(content));
}

function onSayHelloError(jq) {
  const d = jq.responseJSON ? 
            jq.responseJSON : jq;
  $("#hello-output").
    removeClass("success").
    addClass("error").
    val(indent(d));
}

function onSayHelloComplete() {
  $("#hello-spinner").hide();
}

function indent(json) {
  return JSON.stringify(json, null, 2);
}

Естественным развитием такого пользовательского интерфейса является

  1. Добавление средства для генерации различных типов ввода; возможно, полностью заменить текстовую область JSON на правильную HTML-форму.

  2. Разбор и отображение вывода в удобном для понимания виде.

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

{
  "departure-airport": "LIN",
  "arrival-airport"  : "FCO",
  "departure-date"   : "2023-09-01",
  "return-date"      : "2023-09-10",
  "adults"           : 1,
  "children"         : 0,
  "infants"          : 0,
  "currency"         : "EUR"
}

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

A screenshot of another         demo page, for a fictitious flight search API, with a more complicated         payload
Рисунок 4. Реальные полезные нагрузки JSON обычно сложны

Однако пользователям может понадобиться изменить даты, поскольку любая статическая дата отправления или прибытия со временем становится недействительной; а изменение дат требует времени и может привести к дополнительным потерям времени из-за ошибок, допущенных вручную. Одним из решений может стать автоматическое изменение дат в JSON — путём устанавления их, скажем, на 30 дней вперёд. Это позволило бы легко провести быстрый smoke-тест («дымовой» тест) API: просто нажмите кнопку «Поиск рейсов» и посмотрите результаты.

Можно пойти дальше: например, иногда нам нужно проверить цены на рейсы за полгода, иногда за 3 месяца, а иногда — всего за неделю. И это здорово — предоставить пользователю такой пользовательский интерфейс, который позволяет быстро изменить полезную нагрузку JSON путём выбора из выпадающего (drop-down) меню. Если мы сделаем то же самое для других полей ввода — например, для кодов аэропортов, мы избавим пользователя от необходимости искать эти коды, что также занимает драгоценное время.

Обновление части полезной нагрузки ввода при каждом изменении выпадающего меню:

$(document).ready(() => {
  $("#departure-airport").
    change(updateDepartureAirport);
});

function updateDepartureAirport() {
  const searchInput = 
    JSON.parse($("#search-input").val());
  searchInput.departureAirport = 
    $("#departure-airport").val();
  $("#search-input").val(indent(searchInput));
}
Рисунок 5: Добавление HTML-формы для автоматической настройки полезной нагрузки
Рисунок 5: Добавление HTML-формы для автоматической настройки полезной нагрузки

Приведённый выше пользовательский интерфейс позволяет быстро и легко изменять полезную нагрузку JSON, не требуя от пользователя специальных знаний. По-прежнему можно просмотреть сгенерированный JSON; и пользователь может изменить его напрямую, если он хочет протестировать кейс, который не охватывается HTML-формой.

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

The same page, now showing part of a complex         JSON response
Рисунок 6: Ответы в формате JSON тоже имеют тенденцию быть сложными

Сложные пользовательские интерфейсы, такие как эта таблица, можно упростить с помощью библиотеки шаблонов, например ejs.

<script id="table-template" 
        type="text/x-ejs-template">
  <table class="striped">
    <tr>
      <% for (let col=0; 
              col< response.prices.length; col++) { 
      %>
      <th><%= response.prices[0][col] %></th>
      <% } %>
    </tr>
    <% for (let row=1; 
            row< response.prices.length; row++) { 
    %>
    <tr>
      <th><%= response.prices[row][0] %></th>
      <% for (let col=1; 
              col< response.prices.length; col++) { 
      %>
      <td><%= response.prices[row][col] %></td>
      <% } %>
    </tr>
    <% } %>
  </table>
</script>

function onSearchSuccess(content, textStatus, jq) {
  $("#search-output").removeClass("error").
    addClass("success").val(indent(content));
  buildPriceTable();
}

function buildPriceTable() {
  const source = $("#table-template").text();
  const template = ejs.compile(source);
  try {
    const responseText = $("#search-output").val();
    const response = JSON.parse(responseText);
    const htmlTable = template({response: response});
    $("#search-output-table").html(htmlTable);
  } catch (exception) {
    console.log(exception);
    $("#search-output-table").html("");
  }
}

Людям будет сложно разобраться с матрицей цен в JSON — поэтому мы можем распарсить JSON и преобразовать его в удобную HTML-таблицу.

Again the same page, now with an HTML         table, presenting the JSON response in an easier-to-read way
Рисунок 7: Парсинг ответа и представление его в удобном для чтения формате

Простая HTML-таблица может значительно облегчить техническим и нетехническим пользователям проверку результатов работы API.

Общие вопросы

Почему бы вместо этого не использовать Swagger UI?

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

  • Входная и выходная полезная нагрузка в Swagger UI ограничена JSON: не получится сделать её более читабельной.

  • Не очень удобен для пользователей без технических знаний.

  • Он может обслуживать только статические полезные нагрузки; но что, если вам нужно предоставлять произвольный идентификатор при каждом вызове? Что, если полезная нагрузка должна содержать текущую дату? Пользователь должен помнить, как исправить полезную нагрузку вручную, и он должен знать, как это сделать. С помощью небольшого фрагмента кода на JavaScript это легко можно сделать автоматически в демонстрационном фронтенде.

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

Нужно ли настраивать специальную сборку с помощью npm?

Если ваш фронтенд использует команду выделенной сборки, то у вас появляется дополнительный шаг в локальном цикле «редактирование-компиляция-запуск-тестирование»: это делает цикл медленнее. Кроме того, это усложняет автоматизацию непрерывной интеграции и доставки: теперь ваш репозиторий исходного кода создаёт два артефакта вместо одного; вам нужно собрать и развернуть оба. По этим причинам я не рекомендую это делать. Если вы привыкли к «большим» фронтенд-фреймворкам, таким как Angular, вы наверняка удивитесь, как много можно сделать, просто загрузив jQuery или React во встроенный тег <script>.

Разве мы не делаем работу, о которой клиент не просил?

Демонстрационный фронтенд улучшает некоторые кросс-функциональные свойства продукта, которые клиент оценит: как минимум, тестируемость продукта и опыт разработчика, а значит, скорость разработки; но есть и другие кросс-функциональные свойства, на которые можно повлиять.

Расскажу вам одну историю: однажды мы занимались переписыванием API продукта. В этом продукте вызов API мог привести к десяткам обращений к другим downstream сервисам. Каждый из этих downstream вызовов мог быть неудачным в смысле HTTP, возвращая код состояния ошибки HTTP; и мог быть неудачным логически, возвращая код логической ошибки в полезной нагрузке ответа. Учитывая, что любой из этих десятков downstream вызовов, по-разному завершившихся неудачей, мог привести к другому, неожиданному результату в ответе нашего API — стало ясно, что нам нужен быстрый способ увидеть, что происходит, когда наша система взаимодействует с downstream сервисами. Поэтому мы расширили демонстрационный фронтенд отчётом о взаимодействии всех downstream сервисов, показывая запрос и ответ от каждого downstream вызова в ответ на один вызов нашего API.

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

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

Идём дальше

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

В этом git-репозитории есть пример кода, который можно использовать в качестве отправной точки; все скриншоты оттуда.


Приходите на открытые уроки, которые пройдут в рамках набора на курс "JavaScript QA Engineer":

  • 5 июня: Тестирование API. Валидируем http запросы. На этом уроке применим json схемы в проекте, поговорим про документирование API, попробуем ответить на вопрос: RAML или Swagger? Также рассмотрим популярные библиотеки для валидации. Записаться

  • 18 июня: Пишем UI тест с помощью Playwright. Поговорим про локаторы, рассмотрим особенности написания тестов при помощи Playwright. Напишем автотест. Записаться

Комментарии (0)