В своих предыдущих постах я показал, как GraphQL может ускорить разработку, создавая гибкие / эффективные API и вписываясь в архитектуру микросервисов здесь. Но до этого момента я разрабатывал без каких-либо тестов, поскольку это были просто примеры кода, но если мы хотим сделать код производственного качества, мы должны иметь возможность тестировать API.

Базовый тест в памяти

HotChocolate, основная платформа .net для GraphQL, достаточно любезна, чтобы предоставить нам простой способ выполнения запроса GraphQL, чтобы мы могли запустить запрос, получить ответ JSON и затем утверждать свойства ответа по своему усмотрению.

Мы можем полностью выполнить тестовое покрытие только с помощью XUnit, как в примере выше. Но, на мой взгляд, объем тестов станет достаточно большим и сложным, и нам понадобится лучший способ выражения требований.

Чуть более реалистичный сценарий:

Каждый цвет показывает, откуда данные поступают из какого интерфейса; это могут быть простые SQL-запросы или реализации NoSql. Что касается договоров, у нас есть:

Вы можете заметить это в ItemRepository и Product Repository; мы не запрашиваем репозиторий для каждого продукта в отдельном вызове, потому что мы хотим пакетировать эти вызовы базы данных. В этом весь смысл использования GraphQL DataLoaders.

Наш поток данных читается как:

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

Как мы могли бы написать тест, который полностью охватил бы этот графовый запрос?

Приемочные испытания, основанные на тестировании - Specflow

Мы попытаемся реализовать приемочные тесты, которые тестируют почти в конце, также известные как Double-loop TDD. Мы также будем использовать Specflow, поэтому, если вам понадобится больше информации о стратегии тестирования или о том, как это сделать, вы можете обратиться к моему сообщению Specflow с .netcore.

Полная кодовая база доступна на Github здесь.

Покрытие запроса может выглядеть примерно так:

Мы определяем наш пользовательский запрос и внешние данные, которые нам предоставят наши репозитории, а затем соответствующим образом утверждаем ответ.

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

Сервер HotChocolate в памяти.

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

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

Заглушить или не заглушить?

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

Никогда не заглушайте! (Сарказм)

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

Вам нужно спросить вот что:

  • Стоит ли лучшее покрытие дополнительных усилий и сложности настройки тестовых данных?
  • Тесты будут немного медленнее. Достаточно ли медленно, чтобы замедлить развитие?
  • А как насчет API других команд? Стоит ли заглушить?

Я вообще склоняюсь к заглушкам, но оставлю вам вот что:

Нет правильных или неправильных ответов, только компромиссы.

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

Что это нам дает?

Это позволяет нам думать о том, как писать конечные точки GraphQL, исходя из подхода «сначала тестирование». По опыту, конечные точки GraphQL могут довольно быстро стать сложными, и вам понадобится способ описать перевод из GraphQL в вызовы базы данных. Кроме того, он предлагает вам быстрый способ проверить, все ли подключено должным образом.

Как сказал Кент Бек, «разработчики заслуживают знать, работает ли их код».

Всего хорошего.

Ресурсы: