Что пишут в блогах

Подписаться

Онлайн-тренинги

Конференции

Что пишут в блогах (EN)

Разделы портала

Про инструменты

.
Ретроспективные уроки автоматизации: тестирование API
12.07.2019 00:00

Автор: Виктор Славчев (Viktor Slavchev)
Оригинал статьи: https://mrslavchev.com/2018/08/06/hindsight-lessons-about-api-testing/
Перевод: Ольга Алифанова

Это последняя часть моего цикла статей о ретроспективных уроках автоматизации. Мне кажется, я достаточно выразил свою философию тестирования и личный опыт. Последнее, чем я хочу поделиться, относится к проекту, с которым я работаю уже год, и касается создания автоматизированных проверок API бэкэнда, который мы сейчас разрабатываем. Мы пишем тесты на PHP во фреймворке Codeception. Я не буду углубляться в особенности фреймворка – я сфокусируюсь на базовых вещах. Итак, вот они – уроки по тестированию API, которые мне нелишним было бы знать заранее – до того, как я изрядно налажал.

Недостаток информации

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

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

  • Что имеет смысл тестировать через API?
  • Похоже ли это тестирование на тестирование через другой интерфейс?
  • Что насчет негативного тестирования?
  • Есть ли дизайн-шаблоны для API-тестов?

Некоторые из этих вопросов так и остались без ответа, а на некоторые я нашел ответ самостоятельно, путем проб и ошибок.

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

Вот несколько полезных материалов для тех, кто начинает тестировать API или хочет узнать об этом больше:

Exploring your APIs with Postman by Amber Race (слайды) – не видел доклада, но слайды с виду довольно полезные.

Service virtualization by Bas Dijkstra (бесплатная книга) – Баз всегда был моим героем во всем, что касается автоматизации и тестирования API

Статья от Smart Bear – полезно и интересно, особенно описание трех уровней тестирования API.

Automating and Testing a REST API – за авторством злобного тестировщика Алана Ричардсона, очень полезный практический материал. Особенно мне понравилась идея приложения как API и API как приложения.

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

Что вы тестируете, тестируя API?

Проект, над которым я работаю – REST API, использующийся SPA-приложением, написанным на React. Я подключился к проекту на ранних стадиях, когда интерфейса и самого приложения еще не существовало, и это было интересной и сложной задачей. Итак, как же, черт побери, тестируется этот API?

Я намекну – когда интерфейса еще нет, вы почти ничего не понимаете в том, как сервисы вызываются и используются. Вот что я мог сделать в плане тестирования:

  • Убедиться, что конечные точки правильно работают по отдельности.
  • Использовать документацию как явный оракул.
  • Уточнить все неясное и расплывчатое.

Все это очень мне помогло, и я вынес из этого опыта несколько ценных уроков:

  • Тестирование конечных точек как изолированных сущностей – это даже не полдела.

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

  • Автоматически генерируемая документация – это не источник информации, а полный отстой.

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

  • Больше багов находишь, задавая вопросы, а не прогоняя тесты

Это то, с чем большинство тестировщиков не может разобраться. Между исследованием и записью тестов нужен баланс. Создавая автоматизированные проверки, мы должны осознавать, что результат – это всего лишь формализованная версия тестирования, проведенного до этого. Путешествие важнее точки назначения, и исследование важнее, чем формализованные скрипты, которые станут его результатом. По моему опыту то, что я делаю, исследуя API, дает мне куда больше информации о проблемах, чем тесты сами по себе. С другой стороны, тесты – это формальная версия этих знаний, и я могу использовать их снова и снова, чтобы убедиться, что ничего не изменилось. Если что-то изменится, я снова приступлю к исследованию. Я буду подробнее говорить об этом в цикле статей "Ретроспективные уроки исследовательского тестирования".

Важные когнитивные барьеры

Еще один интересный факт, с которым я столкнулся, тестируя API: мне было очень трудно разобраться, какие данные (валидные, невалидные, null, пустые строки…) использовать в качестве параметров для наших сервисов.

Пример: одна из конечных точек, относящихся к логину, выдавала ошибку 500 при использовании китайских иероглифов на входе. Я тестировал ее через API, и мне не пришло в голову попробовать использовать иероглифы, хотя тестируй я через интерфейс, я бы обязательно это сделал.

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

  • Что делают эти конечные точки
  • Как они вызываются фронтэндом
  • Какие системы они формируют
  • Как они зависят друг от друга.

Мне кажется, это пригодится абсолютно всем.

Типы тестов

Пробуя различные подходы, я обнаружил, что тесты, имеющие смысл, можно разделить на три группы.

Что мы хотим знать об API в плане тестирования?

  • Что все конечные точки функционируют.
  • Что возвращаются корректные данные.
  • Что конечные точки может использовать клиент (что угодно, пользующееся ими).

Исходя из этих задач, я придумал следующие типы тестов:

Проверки кодов статуса

Цель этих проверок – просто убедиться в функционировании конечной точки.

К примеру, ваша спецификация гласит, что у вас будут такие коды:

200 – успешно

400 – неверные данные

404 – недоступный ресурс, и т. д.

Попробуйте создать простенькие тесты и убедиться, что каждый из этих кодов можно получить в ответе.

Цель:

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

Несколько советов:

  • Они дают очень ограниченную информацию, обращайтесь с ними, как с переключателями вкл/выкл.
  • Ситуации с ошибкой 400 могут сильно варьировать, поэтому убедитесь, что вы покрыли наиболее значимые из них. Некоторые из них имеет смысл тестировать в контексте сценариев.

Структурные проверки

Коды статуса – это здорово, но они просто сообщают, работает ли сервис так, как гласят формальные правила его работы. Клиенту важны данные, а код 200 или 400 ничего про данные не говорит. К тому же проверка кодов статуса для GET-методов вообще практически ничего нам не сообщает.

Я решил, что полезно будет протестировать, что возвращаются правильные данные.

Для этого мы использовали встроенный в Codeception метод seeResponseMatchesJsonType. Он смотрит в ваш json-ответ, используя json-path (похоже на XPath, но используется для json) и проводит валидацию того, что определенная переменная вашего ответа содержит данные типа Х.

Вот пример с сайта Codeception:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

<?php

// {'user_id': 1, 'name': 'davert', 'is_active': false}

$I->seeResponseMatchesJsonType([

'user_id' => 'integer',

'name' => 'string|null',

'is_active' => 'boolean'

]);


// narrow down matching with JsonPath:

// {"users": [{ "name": "davert"}, {"id": 1}]}

$I->seeResponseMatchesJsonType(['name' => 'string'], '$.users[0]');

?>

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

  • integer:>{val} – проверяет, что integer больше, чем {val} (работает также с плавающей точкой и строками).
  • integer:<{val} – проверяет, что integer меньше, чем {val} (работает также с плавающей точкой и строками).
  • string:url – проверяет, что значение –валидный url.
  • string:date – проверяет, что значение – это дата в формате JavaScript: https://weblog.west-wind.com/posts/2014/Jan/06/JavaScript-JSON-Date-Parsing-and-real-Dates
  • string:email – проверяет, что значение – это валидный емейл согласно http://emailregex.com/
  • string:regex({val}) – проверяет, что строка соответствует регулярному выражению внутри {val}

Цель:

Может быть полезным для валидации данных, возвращаемых GET-сервисами, так как они зачастую содержат большое количество информации.

Главный плюс таких проверок для меня в том, что они предупредят, если данные не возвращены. К примеру, вы делаете post-запрос к конечной точке Х и получаете ответ с ID записи в базе данных. Отсутствие этого ID сигнализирует о серьезных проблемах.

Несколько советов:

  • Если дата варьируется или необязательна, у вас могут быть проблемы – в проверке нет возможности сделать ее опциональной, поэтому или вы это проверяете, или пропускаете.
  • Вы можете проверять тип, формат, интервал, регулярное выражение, но не точное совпадение (кроме регулярных выражений).
  • Когда в ответе много вложенных уровней, тест и json path могут стать довольно запутанными.

Проверки сценариев

Я вдохновлялся идеей Алана Ричардсона о приложении как API. Тесты, вызывающие несколько разных конечных точек так, как это делало бы приложение, нужны для полноты понимания API и его использования.

Цель:

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

Основная идея – разработать простые сценарии. Например, вы открываете пользовательский профиль и обновляете его. В качестве теста вы используете GET-запрос, получаете данные и отправляете их как POST.

Несколько советов:

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

Дизайн фреймворка

Об это я бился довольно мучительно. Я пытался найти альтернативу шаблонам для API-тестов, но так их и не нашел. В результате я разработал собственный дизайн для фреймворка, который удовлетворяет нашим нуждам. Вот чем я руководствовался:

  • Разделение логики тестов и логики фреймворка.

Это часто используется в шаблонах для UI-тестов вроде Page Object. Пишите фреймворк так, чтобы ваши тесты использовали абстракции уровнем выше, чем встроенные в инструмент функции.

  • Акселераторы

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

  • Генераторы тестовых данных

В этом случае мне повезло – я заручился поддержкой разработчиков, которые помогли создать инструменты для генерации данных и записей в БД, которые могли понадобиться при тестировании API. Важный урок – сотрудничайте со своими разработчиками, они могут серьезно улучшить вашу работу множеством способов помимо исправления багов.

Пример: если я собираюсь тестировать WordPress API, мне понадобится инструмент, генерирующий посты, или пользователь с админскими правами.

  • Использование парадигм программирования и ООП

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

Если у вас есть возможность провести рефакторинг фреймворка – сделайте это, не откладывайте это на потом. Это может показаться чрезмерной тратой времени, но это оправданная трата – вы вкладываетесь в улучшенную поддержку тестов, более высокую скорость, повышенную надежность.

Будет ошибкой пытаться создать идеальный фреймворк с первого раза. Возможно, если вы занимаетесь этим лет двадцать, это реализуемо, но по моему опыту, создание кода – органичный процесс, у него есть жизненный цикл и естественная эволюция. Если вы насильно ускорите эволюцию, то породите мутанта.

Я это еще не делал, но это может пригодиться

  • Null-вставка: создайте метод или класс, который вызывает метод API и передает null в параметрах POST или URL. Поверьте, так можно найти множество отличных багов.
  • Вставка плохих данных: как и в примере выше, имеет смысл создать инструмент, который вставляет плохие данные в параметры так, чтобы это можно было контролировать и отслеживать. К примеру, это могут быть данные из этого списка.

Вот краткий список уроков, которые я извлек из тестирования API за то недолгое время, что я этим занимаюсь. Я не считаю себя экспертом, поэтому поправьте меня или дополните, я буду рад!

Обсудить в форуме