Опубликован: 27.01.2016 | Доступ: свободный | Студентов: 914 / 58 | Длительность: 23:07:00
Лекция 6:

Моделирование пользователей

Валидация наличия

Возможно самой элементарной валидацией является валидация наличия, которая просто проверяет что данный атрибут имеется в наличии. Например, в данном разделе мы убеждаемся что оба 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

С нашим комплектом тестов, вновь проходящим, мы можем идти дальше, к более интересной валидации: валидации формата электронной почты.

Вадим Обозин
Вадим Обозин

Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора?