В продолжение моих предыдущих постов — Создание вашего первого приложения на Rails: модели, представления и контроллеры — я собираюсь рассказать о простом подходе, основанном на тестировании, для добавления новой функции в наше приложение для сокращения URL-адресов, Shorty.
Чтобы протестировать этот процесс, мы сделаем сайт более похожим на реальное сокращение URL-адресов, то есть мы протестируем и реализуем способ генерации простого короткого кода, который представляет сокращаемый URL-адрес.
Сама функциональность относительно проста (в нашем случае мы конвертируем id обратно и вперед между буквенно-цифровым представлением), но это хорошая возможность для применения подхода, основанного на тестировании. Для этого мы будем использовать встроенные средства тестирования Rails, а в конце я также приведу ссылку на еще несколько вариантов для изучения в свое время.
Сегодня мы рассмотрим часть модели, а в следующем посте мы интегрируем наши сокращенные URL-адреса и напишем несколько тестов контроллера.
Что нам нужно
Чтобы это работало, нам нужно реализовать три отдельные вещи:
- Способ преобразования заданного сохраненного URL в короткий код
- Способ конвертировать короткий код в сохраненный URL
- Новый, более короткий маршрут для получения URL с этого.
Приступая к работе, мы хотим открыть существующие тестовые файлы. Когда мы сгенерировали нашу модель URL в первой части, Rails также сгенерировал для нас test/unit/url_test.rb
в test/unit/url_test.rb
. Открыв его и посмотрев, вы должны увидеть нечто похожее на:
require 'test_helper' class UrlTest < ActiveSupport::TestCase # Replace this with your real tests. test "the truth" do assert true end end
Это общая структура модульного теста в Rails 3 — метод test
позволяет нам объявлять метод (у нас также есть setup
и teardown
для поддержки обобщенной среды для наших тестов), и мы используем assert
(например, вызов метода assert
в код выше). Rails (и Test::Unit
, интегрированные в rails инфраструктуры тестирования) поставляются с несколькими утверждениями из коробки, которые мы можем использовать — для списка, посмотрите методы, начинающиеся с assert_
в rails api docs и эту старую assert_
для некоторых из утверждения стандартного тестового блока.
Письменные тесты
Далее мы добавим несколько тестовых заглушек — пустые тесты, которые мы можем заполнить позже. Для этого нам нужно выработать именно то, что мы хотим протестировать, в самых простых терминах. Внутри класса теста URL замените существующие строки теста следующим:
test 'creating a url lets us fetch a short code' test 'existing urls have short codes' test 'converting a short code to an id' test 'finding a url from a known short code' test 'finding a url from a invalid short code raises an exception'
Затем из командной строки мы можем запустить эти пустые тесты, чтобы убедиться, что они не пройдены, выполнив следующее из каталога нашего приложения:
rake test:units
Поскольку мы не сделали ничего, кроме написания их имен, у нас должно быть четыре ошибки.
Теперь мы пройдем наши тесты 1 и напишем их. Для начала заполните их по очереди, заменив тестовую заглушку:
test 'creating a url lets us fetch a short code' do my_url = Url.create(:url => 'http://google.com/') # The url should have a short code assert_present my_url.short_code end test 'existing urls have short codes' do my_url = Url.create(:url => 'http://google.com/') # Force a fetch from the datbase found_url = Url.find(my_url.id) assert_present found_url.short_code assert_equal my_url.short_code, found_url.short_code end test 'finding a url from a known short code' do my_url = Url.create(:url => 'http://google.com/') assert_equal my_url, Url.find_using_short_code!(my_url.short_code) end test 'finding a url from a invalid short code raises an exception' do assert_raises ActiveRecord::RecordNotFound do Url.find_using_short_code! 'non-existant-short-code' end end
В каждом из тестов мы тестируем некоторые аспекты ожидаемого поведения модели:
- В нашем первом тесте мы проверяем, что после создания URL-адреса он имеет короткий код, вызывая метод
short_code
и вызываяassert_present
со своим значением. - Во втором тесте мы создаем URL-адрес, принудительно перезагружаем его из базы данных (чтобы имитировать его выборку в более поздний момент времени), а затем проверяем, что он также имеет короткий код и, что более важно, тот же самый короткий найденный объект код.
- В третьем тесте мы создаем URL-адрес и проверяем, что при извлечении его из базы данных (используя наш несуществующий в настоящее время метод
find_using_short_code!
) Он будет возвращать тот же URL-адрес. - В нашем последнем тесте мы проверяем, что, когда мы даем ему недопустимый короткий код, возникает ожидаемое исключение.
Вернемся к командной строке и снова rake test:units
наши тесты с помощью rake test:units
, мы все равно должны увидеть 4 ошибки. Это хорошо — это значит, что у нас есть тесты, но мы еще не реализовали их.
Проходя наши тесты
Теперь мы собираемся пройти наши тесты. Для этого нам нужно реализовать два метода. Сначала мы используем short_code
экземпляра short_code
класса Url
а затем find_using_short_code!
метод класса.
В url.rb
мы добавим метод для генерации короткого кода. На данный момент мы просто будем использовать базовое значение id 36 (например, 10
будет a
):
class Url < ActiveRecord::Base validates :url, :presence => true def short_code id.to_s 36 end end
Повторно запустив наши тесты, мы увидим, что 2 из 4 наших тестов теперь пройдены. Далее мы реализуем метод, чтобы найти его по идентификатору, выполнив обратное преобразование (взяв число из базового значения 36):
class Url < ActiveRecord::Base validates :url, :presence => true def short_code id.to_s 36 end def self.find_using_short_code!(short_code) find short_code.to_i(36) end end
Запустив наши тесты в последний раз, мы увидим, что все они теперь проходят — теперь мы можем получать и генерировать короткие коды.
Следующие шаги
В следующем посте мы расскажем, как интегрировать наши короткие коды в контроллер и как его протестировать.
На данный момент, посмотрите, можете ли вы написать еще несколько тестов для себя — Один случай, который стоит рассмотреть, это то, что происходит, когда у вас нет идентификатора в модели? (например, он еще не был сохранен).
Внимательные читатели могут также захотеть прочитать о том, как интегрировать RSpec в свое приложение для альтернативного синтаксиса и подхода к написанию тестов. Для дополнительного кредита вы также можете изменить способ конвертации между идентификаторами и короткими кодами, например, вместо использования 36 (от 0 до 9 и от a до b), вы также можете указать разницу между строчными и прописными буквами.