На этом этапе, если бы мы снова запустили спецификации, мы бы получили ответ об ошибке 404
, поскольку мы не делали ничего, связанного с сохранением каких-либо настроек статьи. Давайте сделаем это дальше.
Чтобы сохранить целенаправленность и простоту, мы просто собираемся выполнять вставки необработанного SQL для целей этой главы. Не стесняйтесь определять некоторые абстракции и вспомогательные методы, а также использовать стороннюю библиотеку приборов — или что-то еще — если хотите.
Поскольку мы автоматически очищаем нашу таблицу после каждого тестового примера, мы можем свободно вставлять любые данные, которые требуются для нашего конкретного тестового примера. В нашем случае нам нужно вставить статью с идентификатором 10
. Нам также следует сделать некоторые утверждения против ответа, чтобы убедиться, что это то, что мы ожидаем. Обновите наш тест статьи GET
, чтобы он выглядел так:
def test_get_article : Nil
DATABASE.exec <<-SQL
INSERT INTO "articles" (id, title, body, created_at,
updated_at) OVERRIDING SYSTEM VALUE
VALUES (10, 'TITLE', 'BODY', timezone('utc', now()),
timezone('utc', now()));
SQL
response = self.get "/article/10"
response.status.should eq HTTP::Status::OK
article = JSON.parse response.body
article["title"].as_s.should eq "TITLE"
article["body"].as_s.should eq "BODY"
end
Поскольку в наших таблицах для первичного ключа (PK)
используется GENERATED ALWAYS AS IDENTITY
, нам необходимо включить OVERRIDING SYSTEM VALUE
в наши инструкции INSERT
, чтобы мы могли указать нужный идентификатор.
В нашем тесте статьи GET
мы утверждаем, что запрос прошел успешно и возвращает ожидаемые данные. Мы также можем протестировать поток языка гипертекстовой разметки (HTML), установив заголовок
def test_get_article_html : Nil
DATABASE.exec <<-SQL
INSERT INTO "articles" (id, title, body, created_at,
updated_at) OVERRIDING SYSTEM VALUE
VALUES (10, 'TITLE', 'BODY', timezone('utc', now()),
timezone('utc', now()));
SQL
response = self.get "/article/10", headers: HTTP::Headers
{"accept" => "text/html"}
response.status.should eq HTTP::Status::OK
BODY response.body.should contain "
end
Мы также могли бы легко протестировать создание статьи, например:
def test_post_article : Nil
response = self.post "/article", body: %({"title":"TITLE",
"body":"BODY"})
article = JSON.parse response.body
article["title"].as_s.should eq "TITLE"
article["body"].as_s.should eq "BODY"
article["created_at"].as_s?.should_not be_nil
article["id"].raw.should be_a Int64
end
Независимо от того, как вы это сделаете, в конечном итоге наши интеграционные тесты контроллера статьи оказались довольно простыми и мощными. Они предоставляют средства для тестирования всего потока запроса, включая прослушиватели, преобразователи параметров и обработчики форматов. Он также позволяет тестировать любую пользовательскую логику сериализации или проверки как часть полезной нагрузки запроса/ответа.
Резюме
Тесты — это одна из тех вещей, написание которых может показаться пустой тратой времени, но в конечном итоге окупается в виде выигранного времени за счет предотвращения попадания ошибок в производство. Чем раньше вы получите тестовое покрытие типа, тем лучше.
В этой главе мы узнали, как использовать модуль Spec
для написания модульных тестов и компонент Athena::Spec
для написания интеграционных тестов. Поскольку это два наиболее распространенных типа тестов, понимание того, как писать хорошие тесты, а также изучение преимуществ того, почему написание тестов является такой хорошей идеей, может оказаться невероятно полезным для обеспечения общей надежности приложения.
В следующей главе мы рассмотрим еще одну вещь, которая не менее важна, чем тесты, — как документировать ваш код/проект.
15. Документирование кода