Пост в продолжение темы экспериментальных решений (https://habrahabr.ru/post/350382/), откуда будет переиспользован код для примера. В прошлом посте я затронул тему, как можно написать тесты на простой сервис, когда он выступает в роли черного ящика и из кода теста у нас нет прямого доступа к коду тестируемой программы. Ещё раз остановлюсь на том, что тестируемый сервис был реализован на языке Go, а тесты к сервису на языке Ruby и фрэймворке для тестирования RSpec. Стэк был выбран из собственных предпочтений и не имеет ключевого значения к рассматриваемой теме. В этой статье хочу рассмотреть вопрос документирования API, вновь используя не совсем стандартное решение.
Существует несколько основных подходов к написанию документации:
- Просто пишем документацию вручную.
- Автоматическая генерация документация по комментариями в коде.
- Автоматическая генерация документации по коду.
В первом случае минусы очевидны, необходимо вручную поддерживать документацию, к тому же текст документации отделён от кода. Во втором случае документация чаще всего генерируется из комментариев к классам, методам, функциями. В данном случае гораздо проще поддерживать актуальное описание, к тому же могут быть дополнительные проверки, вроде того, что сигнатура функции совпадает с описанием параметров в комментарии. Автоматическая генерация по коду должна исключить проблему поддержки актуального описания методов API. Данные способы широко применяются для документирования библиотек. Однако автоматическая генерация крайне редко встречается для WEB API, одна из возможных причин — все такие генераторы являются фрэймворко зависимые и требуют определённой поддержки рефлексии от веб фрэймворка и языка программирования.
Теперь кратко рассмотрим несколько менее распространенных способов составления (и использования) документации:
- Пишем машиночитаемую документацию, генерируем по ней код шаблон кода сервера и клиента.
- Имея машиночитаемую документацию, проверяем в тестах соответствие поведения API на описание в документации, например, что ответ метода соответствует тому, что написано в документации.
Сразу поясню, зачем может понадобится документация к API (машиночитаемая в том числе) до того, как написана сама реализация API. Чаще всего такая ситуация возникает, когда ведётся одновременная разработка как сервера API, так и клиентского кода. Имея машиночитаемую документацию на этапе разработки клиента, появляется возможность сгенерировать как сам API клиент, так и сервер, эмулирующий реальные ответы.
Ещё хочу отметить, что такие протоколы как GRPC, Apache Thrift и подобные фактически заставляют сначала написать (а потом и поддерживать) машиночитаемое описание API и только потом писать реализацию, что несомненно может нести некоторый раздражающий эффект от необходимости постоянной правки файлов с описанием протокола, но с другой стороны мы всегда уверены, что как минимум описание сигнатур соответствует реальности.
И последний (в данном тексте) способ получения документации:
- Генерируем документацию к методам API по реальному ответу от сервера. Запросы к серверу кодируются к тестах.
И вот это уже тема вынесенная в заголовок. Так как код тестов является неотъемлемой частью тестируемой системы, можно достаточно дёшево возложить на тесты дополнительный функционал. Выделю плюсы этого метода:
- Внутри документации получаем 100% актуальные примеры ответов от методов API, и все возможные сценарии использования, если они отдельно покрыты тестами.
- Мы можем автоматически сгенерировать документацию не внося изменений в исходный код сервиса. Таким образом мы больше не зависим от поддержки авто документации конкретным фрэймворком или технологии,
- Мы вообще можем не иметь доступ к исходному коду сервиса, но можем получить в том числе машиночитаемую документацию и использовать все её преимущества.
В данном примере для документирования API будет использована спецификация Swagger, пропущу общее описание данного инструмента, так как таких обзоров достаточно. Но отмечу, что это машиночитаемая документация, позволяющая генерировать как уже человеческую документацию, так и шаблоны серверного и клиентского кода для работы с описанным API.
На просторах сети были найдены руби гемы rspec-rails-swagger и rswag. Оба к сожалению имеют хоть и минимальную, но привязку к rails. Гемы имеют вполне подробную документацию с примерами и достаточно простой код. В качестве эксперимента я отвязал гем rspec-rails-swagger от Rails github.com/ZurgInq/rspec-rails-swagger/commit/9051854340780d9b9a60811f2f8d230cc99ef570 и подключил к существующим функциональным тестам.
Описание теста для генерации документации к методу выглядит следующим образом:
describe 'swagger_docs' do
let(:movies_resp_body) { File.read('spec/fixtures/movies.json') }
path '/movies' do
operation "GET", summary: "respond 200 OK" do
parameter :rating, in: :query, type: :string, required: false, description: "filter by rating"
response 200, description: "successful" do
schema(
type: :array,
items: {
type: :string,
}
)
end
end
end
end
Данный код запускает тесты на исполнение и используют расширение синтаксиса Rspec для указания мета информации которая будет использована при генерации swagger файла.
Запускаем rspec через длинную команду:
bundle exec rspec -f RSpec::Rails::Swagger::Formatter --order defined -t swagger_object
Флаг -t фильтрует запускаемые тесты только теми, которые используют специальный синтаксис гема. Флаг -f подключает «форматтер» для вывода результат в виде swagger json файла.
Выхлоп команды выдаст валидный swagger файл который можно загрузить в swagger-ui или попробовать использовать для генерации клиента к API.
В конечном итоге мы имеем функциональные тесты на фрэймворке Rspec, попутно сгенерировав swagger документацию, из которой можно быстро получить болванку API клиента для любого другого языка программирования. Полный листинг примера github.com/ZurgInq/rspec-tutorial
Резюмируя:
- Автоматическая документация гарантирует некоторое минимальное совпадение ваших ожиданий от API и суровой изменчивой реальности.
- В некоторых случаях разработчик физически вынужден вначале описать методы API и только потом их реализацию (протоколы GRPC, Apache Thrift).
- Автоматически сгенерированная машиночитаемая документация может упростить жизнь как на этапе разработки, так и на этапе поддержки и работы с API.
- Используя специальные инструменты можно генерировать машиночитаемую документацию не внося изменения в исходный код.
Автор: ZurgInq