struct CalculatorSpec < ASPEC::TestCase
@target : Calculator
def initialize : Nil
@target = Calculator.new
end
def test_add
@target.add(1, 2).should eq 3
end
test "subtract" do
@target.subtract(10, 5).should eq 5
end
end
Каждый метод, начинающийся с test_, сводится к методу #it из модуля Spec. Макрос test также можно использовать для упрощения создания этих методов. Поскольку тесты определяются внутри структуры, вы можете использовать наследование и/или композицию, чтобы разрешить повторное использование логики для групп связанных тестов. Это также позволяет проектам предоставлять абстрактные типы, что упрощает создание тестов для определенных типов. Именно такой подход Athena Framework использовала в отношении своего типа ATH::Spec::APITestCase. См. https://athenaframework.org/Framework/Spec/APITestCase/ и https:// athenaframework.org/Spec/TestCase/#Athena::Spec::TestCase для получения дополнительной информации.
Возвращаясь к интеграционным тестам нашего блога, давайте начнем с тестирования контроллера статей, создав для их хранения новый файл: spec/controllers/article_controller_spec.cr. Затем добавьте в него следующий контент:
require "../spec_helper"
struct ArticleControllerTest < ATH::Spec::APITestCase
end
Мы также можем удалить файл spec/blog_spec.cr по умолчанию.
APITestCase предоставляет метод #request, который можно использовать для отправки запросов к нашему API, а также предоставляет вспомогательные методы для распространенных команд протокола передачи гипертекста (HTTP), таких как #get и #post. Он также реализован таким образом, что фактический тип HTTP::Server не требуется. Это позволяет тестировать логику приложения быстрее и надежнее. Однако, как упоминалось в начале этой главы, тестирование E2E также важно для проверки полного взаимодействия системы.
Начнем с тестирования конечной точки для получения конкретной статьи по идентификатору (ID), добавив следующий метод в ArticleControllerTest:
def test_get_article : Nil
response = self.get "/article/10"
pp response.status, response.body
end
Прежде чем мы сможем опробовать этот тестовый пример, нам сначала нужно сообщить spec/spec_helper.cr об абстрактном типе тестового примера, а также настроить его для запуска наших тестов на основе компонентов Athena::Spec. Обновите spec/spec_helper.cr, чтобы он выглядел так:
require "spec"
require "../src/blog"
require "athena/spec"
ASPEC.run_all
Помимо модуля Spec и исходного кода нашего блога, нам также требуются помощники по спецификациям, предоставляемые компонентом Framework. Наконец, нам нужно вызвать ASPEC.run_all, чтобы убедиться, что эти типы тестов действительно выполняются. Однако, поскольку компонент Athena Spec не является обязательным, нам необходимо добавить его в качестве зависимости разработки, добавив следующий код в ваш файл shard.yml с последующей установкой шардов:
development_dependencies:
athena-spec:
github: athena-framework/spec
version: ~> 0.2.3
Запуск crystal spec выявил проблему с нашей тестовой установкой. Ответ на запрос полностью зависит от состояния вашей базы данных разработки. Например, если у вас нет созданной/работающей базы данных, вы получите HTTP-ответ 500. Если у вас есть статья с идентификатором 10, вы получите ответ 200, поскольку все работает как положено.
Смешивание данных базы данных разработки с данными тестирования — не лучшая идея, поскольку это усложняет управление и приводит к менее надежным тестам. Чтобы облегчить эту проблему, мы воспользуемся тестовой схемой, созданной еще в Главе 9 «Создание веб-приложения с помощью Athena». Файл настройки языка структурированных запросов (SQL) устанавливает владельцем того же пользователя, что и наша база данных разработки, чтобы мы могли повторно использовать одного и того же пользователя. Поскольку мы также настроили использование переменной среды, нам не нужно менять какой-либо код для поддержки этого. Просто export DATABASE_URL=postgres://blog_user:mYAw3s0meB\!log@ localhost:5432/postgres?currentSchema=test, и все должно работать. Еще одна вещь, которую нам нужно будет сделать, — это создать таблицы, а также создать/удалить данные о приборах. Мы собираемся немного схитрить и использовать для этого необработанный API Crystal DB, поскольку он немного выходит за рамки нашего типа EntityManager.