Статьи

Почему наличие API имеет значение: тестирование

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

Контекст: архитектура

Это приложение разделено между несколькими компонентами на стороне сервера, которые взаимодействуют друг с другом, иногда асинхронно. Однако только один из них стоит снаружи.

Пользовательский интерфейс rich-client, написанный на JavaScript, запускается в браузере и обращается к этому порту приложения. Он связывается со стороной сервера через REST-подобный API, который как таковой не имеет персистентности сеанса. Например, передаваемые данные содержатся в параметрах POST, но в основном в теле запросов PUT и GET в форме JSON.

Поведение может быть немного обусловлено самим HTTP, поскольку он обеспечивает семантику, которую должен соблюдать браузер (перенаправления с учетом заголовка Location, семантики POST и GET и т. Д.). В этой архитектуре   нет ничего нового и революционного, но удивительно, что она реализована последовательно. 

Цель

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

Как

Что ж, после того, как API, похожий на REST, доступен, вы можете очень легко принять библиотеку HTTP для вашего языка и начать писать тесты, как если бы вы были браузерным ботом:

  • сделать HTTP-вызовы : POST, GET, PUT или DELETE в соответствии с требуемым действием.
  • Кодируйте и декодируйте JSON (или XML) в свою собственную среду тестирования, которая может даже не быть написана на том же языке, что и приложение (это будет крайний случай).
  • Напишите утверждения о возвращаемых данных, а также о состоянии диалога: коды состояния, заголовки ответа HTTP и все, что можно указать в отношении API.

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

Решенные проблемы

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

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

while (!condition()) {
   sleep(1);
   seconds++;
   if (seconds > timeout) {
     fail("We tried waiting for $condition to be fulfilled, but after $timeout seconds there was still no result, so we declare the test failed.");
   }
} 

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

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

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

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

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

Выводы

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