Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
Моделирование пользователей
Валидация наличия
Возможно самой элементарной валидацией является валидация наличия, которая просто проверяет что данный атрибут имеется в наличии. Например, в данном разделе мы убеждаемся что оба name и email поля заполнены прежде чем пользователь будет сохранен в базе данных. В Разделе 7.3.3 мы увидим как распространить это требование на форму регистрации новых пользователей.
Мы начнем с теста на наличие атрибута name. Хотя первым шагом в TDD является написание провального теста (Раздел 3.2.1), в данном случае мы пока недостаточно знаем о валидации для того чтобы написать годный тест, так что мы вначале напишем валидацию и немного поиграем с ней в консоли, - чтобы побольше узнать о ней. Затем мы закомментируем валидацию, напишем провальный тест и проверим что раскомментирование валидации приводит к прохождению теста. Эта процедура может показаться излишне педантичной для такого простого теста, но я видел много "простых" тестов, которые на самом деле тестировали неправильные вещи; дотошность в TDD это просто единственный способ быть уверенными в том что мы тестируем правильные вещи. (Такая техника закомментирования также полезна при спасении приложения, код которого уже написан, но—quelle horreur! — (# какой ужас - фр.) не имеет тестов.)
Способ валидации наличия атрибута имени заключается в применении метода validates с аргументом presence: true, как это показано в Листинге 6.6. Аргумент presence: true это одноэлементный хэш опций; вспомните из Раздела 4.3.4 что фигурные скобки являются необязательными при передаче хеша в качестве последнего аргумента в методе. (Как отмечено в Разделе 5.1.1, использование хэшэй опций это очень распространенный прием в Rails.)
class User < ActiveRecord::Base validates :name, presence: true endЛистинг 6.6. Валидация наличия name атрибута. app/models/user.rb
Листинг 6.6 возможно выглядит как магия, но validates это просто метод. Эквивалентная Листингу 6.6 формулировка с применением скобок выглядит следующим образом:
class User < ActiveRecord::Base validates :name, presence: true end
Давайте заскочим в консоль чтобы увидеть эффект добавления валидации к нашей модели User:9Я опускаю выводы консольных команд когда они не особенно поучительны, например, результат User.new.
$ rails console --sandbox >> user = User.new(name: "", email: "mhartl@example.com") >> user.save => false >> user.valid? => false
Здесь user.save возвращает false, указывая на провальное сохранение. В заключительной команде мы используем valid? метод, который возвращает false когда объект приводит к сбою одной или более валидаций, и true когда все валидации проходят. В данном случае у нас есть только одна валидация, таким образом, мы знаем, какая именно провалилась, но все же не лишним будет в этом убедиться с помощью объекта errors, генерируемого при отказе:
>> user.errors.full_messages => ["Name can't be blank"]
(Сообщение об ошибке - подсказка, говорящая о том что Rails проверяет наличие атрибута, используя blank? метод, который мы видели в конце Раздела 4.4.3.)
Теперь о провальном тесте. Чтобы гарантировать что наш начальный тест перестанет работать, давайте закомментируем валидацию (Листинге 6.7).
class User < ActiveRecord::Base # validates :name, presence: true endЛистинг 6.7. Закомментирование валидации для обеспечения провальности теста. app/models/user.rb
Начальный тест валидации представлен в Листинге 6.8.
require 'spec_helper' describe User do before do @user = User.new(name: "Example User", email: "user@example.com") end subject { @user } it { should respond_to(:name) } it { should respond_to(:email) } it { should be_valid } describe "when name is not present" do before { @user.name = " " } it { should_not be_valid } end endЛистинг 6.8. Провальный тест валидации атрибута name. spec/models/user_spec.rb
Первый новый тест это просто проверка на то что объект @user изначально валиден:
it { should be_valid }
Это еще один пример булевой конвенции RSpec которую мы видели ранее в Разделе 6.2.1: в каждом случае, когда объект отвечает на булевый метод foo?, существует соответствующий тестовый метод с именем be_foo. В данном случае мы можем протестировать результат вызова
@user.valid?
с помощью
it "should be valid" do expect(@user).to be_valid end
Как и прежде, subject { @user } позволяет нам использовать однострочный стиль, что приводит к
it { should be_valid }
Второй тест вначале назначает пользовательскому имени недопустимое значение, а затем проверяет что получившийся объект @user невалиден:
describe "when name is not present" do before { @user.name = " " } it { should_not be_valid } end
Здесь используется блок before для назначения невалидного значения атрибуту name, а затем происходит проверка того что получившийся объект user невалиден.
Теперь необходимо убедиться в том что в данный момент тесты провальны:
$ bundle exec rspec spec/models/user_spec.rb ...F 4 examples, 1 failure
Теперь раскомментируем валидацию (т.е., вернемся от Листинга 6.7 обратно к Листингу 6.6) для того чтобы получить прохождение теста:
$ bundle exec rspec spec/models/user_spec.rb .... 4 examples, 0 failures
Конечно, мы также хотим валидировать наличие адресов электронной почты. Тест (Листинг 6.9 ) походит на аналогичный тест для атрибута name.
require 'spec_helper' describe User do before do @user = User.new(name: "Example User", email: "user@example.com") end . . . describe "when email is not present" do before { @user.email = " " } it { should_not be_valid } end endЛистинг 6.9. Тест для наличия атрибута email. spec/models/user_spec.rb
Реализация практически та же, что и в Листинге 6.10.
class User < ActiveRecord::Base validates :name, presence: true validates :email, presence: true endЛистинг 6.10. Валидация наличия атрибутов name и email. app/models/user.rb
Теперь все тесты должны проходить и валидации "наличия" готовы.
Валидация длины
Мы ограничили нашу модель User требованием имени для каждого пользователя, но мы должны пойти еще дальше: имена пользователей будут отображаться на сайте, таким образом, мы должны будем реализовать некоторое ограничение их длины. С работой, проделанной в Разделе 6.2.2, этот шаг легок.
Мы начнем с теста. В выборе максимальной длины нет ничего хитрого; мы просто примем 50 как разумную верхнюю границу, что означает что имена длиной в 51 символ будут слишком длинными (Листинг 6.11).
require 'spec_helper' describe User do before do @user = User.new(name: "Example User", email: "user@example.com") end . . . describe "when name is too long" do before { @user.name = "a" * 51 } it { should_not be_valid } end endЛистинг 6.11. Тест для валидации длины name. spec/models/user_spec.rb
Для удобства мы использовали "мультипликацию строки" в Листинге 6.11 для создания строки длиной в 51 символ. Мы можем увидеть как это работает, используя консоль:
>> "a" * 51 => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" >> ("a" * 51).length => 51
Тест в Листинге 6.11 должен провалиться. Чтобы заставить его пройти, мы должны знать об аргументе валидации, ограничивающим длину, :length, наряду с :maximum параметром реализуют верхнюю границу (Листинг 6.12).
class User < ActiveRecord::Base validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true endЛистинг 6.12. Добавление валидации длины для name атрибута. app/models/user.rb
С нашим комплектом тестов, вновь проходящим, мы можем идти дальше, к более интересной валидации: валидации формата электронной почты.