class ValidationsTest

Public Class Methods

model_name() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 174
def self.model_name
  ActiveModel::Name.new(self, nil, "Topic")
end
new(*) click to toggle source
Calls superclass method
# File activemodel/test/cases/validations_test.rb, line 183
def initialize(*)
  super
  @call_sequence = []
end

Public Instance Methods

teardown() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 15
def teardown
  Topic.clear_validators!
end
test_acceptance_validator_doesnt_require_db_connection() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 186
def test_acceptance_validator_doesnt_require_db_connection
  klass = Class.new(ActiveRecord::Base) do
    self.table_name = "posts"
  end
  klass.reset_column_information

  assert_no_queries do
    klass.validates_acceptance_of(:foo)
  end
end
test_accessing_instance_of_validator_on_an_attribute() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 306
def test_accessing_instance_of_validator_on_an_attribute
  Topic.validates_length_of :title, minimum: 10
  assert_equal 10, Topic.validators_on(:title).first.options[:minimum]
end
test_callback_options_to_validate() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 179
def test_callback_options_to_validate
  klass = Class.new(Topic) do
    attr_reader :call_sequence

    def initialize(*)
      super
      @call_sequence = []
    end

    private
      def validator_a
        @call_sequence << :a
      end

      def validator_b
        @call_sequence << :b
      end

      def validator_c
        @call_sequence << :c
      end
  end

  assert_nothing_raised do
    klass.validate :validator_a, if: -> { true }
    klass.validate :validator_b, prepend: true
    klass.validate :validator_c, unless: -> { true }
  end

  t = klass.new

  assert_predicate t, :valid?
  assert_equal [:b, :a], t.call_sequence
end
test_does_not_modify_options_argument() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 429
def test_does_not_modify_options_argument
  options = { presence: true }
  Topic.validates :title, options
  assert_equal({ presence: true }, options)
end
test_double_attr_validation_and_error_msg() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 39
def test_double_attr_validation_and_error_msg
  r = Reply.new
  assert r.invalid?

  assert r.errors[:title].any?, "A reply without title should mark that attribute as invalid"
  assert_equal ["is Empty"], r.errors["title"], "A reply without title should contain an error"

  assert r.errors[:content].any?, "A reply without content should mark that attribute as invalid"
  assert_equal ["is Empty"], r.errors["content"], "A reply without content should contain an error"

  assert_equal 2, r.errors.count
end
test_dup_validity_is_independent() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 435
def test_dup_validity_is_independent
  Topic.validates_presence_of :title
  topic = Topic.new("title" => "Literature")
  topic.valid?

  duped = topic.dup
  duped.title = nil
  assert duped.invalid?

  topic.title = nil
  duped.title = "Mathematics"
  assert topic.invalid?
  assert duped.valid?
end
test_errors_conversions() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 214
def test_errors_conversions
  Topic.validates_presence_of %w(title content)
  t = Topic.new
  assert t.invalid?

  xml = t.errors.to_xml
  assert_match %r{<errors>}, xml
  assert_match %r{<error>Title can't be blank</error>}, xml
  assert_match %r{<error>Content can't be blank</error>}, xml

  hash = {}
  hash[:title] = ["can't be blank"]
  hash[:content] = ["can't be blank"]
  assert_equal t.errors.to_json, hash.to_json
end
test_errors_empty_after_errors_on_check() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 112
def test_errors_empty_after_errors_on_check
  t = Topic.new
  assert t.errors[:id].empty?
  assert t.errors.empty?
end
test_errors_on_base() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 81
def test_errors_on_base
  r = Reply.new
  r.content = "Mismatch"
  r.valid?
  r.errors.add(:base, "Reply is not dignifying")

  errors = r.errors.to_a.inject([]) { |result, error| result + [error] }

  assert_equal ["Reply is not dignifying"], r.errors[:base]

  assert_includes errors, "Title is Empty"
  assert_includes errors, "Reply is not dignifying"
  assert_equal 2, r.errors.count
end
test_errors_on_base_with_symbol_message() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 96
def test_errors_on_base_with_symbol_message
  r = Reply.new
  r.content = "Mismatch"
  r.valid?
  r.errors.add(:base, :invalid)

  errors = r.errors.to_a.inject([]) { |result, error| result + [error] }

  assert_equal ["is invalid"], r.errors[:base]

  assert_includes errors, "Title is Empty"
  assert_includes errors, "is invalid"

  assert_equal 2, r.errors.count
end
test_errors_on_nested_attributes_expands_name() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 75
def test_errors_on_nested_attributes_expands_name
  t = Topic.new
  t.errors["replies.name"] << "can't be blank"
  assert_equal ["Replies name can't be blank"], t.errors.full_messages
end
test_exception_on_create_bang_many() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 104
def test_exception_on_create_bang_many
  assert_raise(ActiveRecord::RecordInvalid) do
    WrongReply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
  end
end
test_exception_on_create_bang_many_with_block() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 118
def test_exception_on_create_bang_many_with_block
  assert_raise(ActiveRecord::RecordInvalid) do
    WrongReply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
      r.content = nil
    end
  end
end
test_exception_on_create_bang_with_block() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 110
def test_exception_on_create_bang_with_block
  assert_raise(ActiveRecord::RecordInvalid) do
    WrongReply.create!("title" => "OK") do |r|
      r.content = nil
    end
  end
end
test_invalid_options_to_validate() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 170
def test_invalid_options_to_validate
  error = assert_raises(ArgumentError) do
    # A common mistake -- we meant to call 'validates'
    Topic.validate :title, presence: true
  end
  message = "Unknown key: :presence. Valid keys are: :on, :if, :unless, :prepend. Perhaps you meant to call `validates` instead of `validate`?"
  assert_equal message, error.message
end
test_invalid_record_exception() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 79
def test_invalid_record_exception
  assert_raise(ActiveRecord::RecordInvalid) { WrongReply.create! }
  assert_raise(ActiveRecord::RecordInvalid) { WrongReply.new.save! }

  r = WrongReply.new
  invalid = assert_raise ActiveRecord::RecordInvalid do
    r.save!
  end
  assert_equal r, invalid.record
end
test_invalid_should_be_the_opposite_of_valid() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 269
def test_invalid_should_be_the_opposite_of_valid
  Topic.validates_presence_of :title

  t = Topic.new
  assert t.invalid?
  assert t.errors[:title].any?

  t.title = "Things are going to change"
  assert !t.invalid?
end
test_invalid_using_multiple_contexts() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 57
def test_invalid_using_multiple_contexts
  r = WrongReply.new(title: "Wrong Create")
  assert r.invalid?([:special_case, :create])
  assert_equal "Invalid", r.errors[:author_name].join
  assert_equal "is Wrong Create", r.errors[:title].join
end
test_invalid_validator() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 162
def test_invalid_validator
  Topic.validate :i_dont_exist
  assert_raises(NoMethodError) do
    t = Topic.new
    t.valid?
  end
end
test_list_of_validators_for_model() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 288
def test_list_of_validators_for_model
  Topic.validates_presence_of :title
  Topic.validates_length_of :title, minimum: 2

  assert_equal 2, Topic.validators.count
  assert_equal [:presence, :length], Topic.validators.map(&:kind)
end
test_list_of_validators_on_an_attribute() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 296
def test_list_of_validators_on_an_attribute
  Topic.validates_presence_of :title, :content
  Topic.validates_length_of :title, minimum: 2

  assert_equal 2, Topic.validators_on(:title).count
  assert_equal [:presence, :length], Topic.validators_on(:title).map(&:kind)
  assert_equal 1, Topic.validators_on(:content).count
  assert_equal [:presence], Topic.validators_on(:content).map(&:kind)
end
test_list_of_validators_on_multiple_attributes() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 311
def test_list_of_validators_on_multiple_attributes
  Topic.validates :title, length: { minimum: 10 }
  Topic.validates :author_name, presence: true, format: /a/

  validators = Topic.validators_on(:title, :author_name)

  assert_equal [
    ActiveModel::Validations::FormatValidator,
    ActiveModel::Validations::LengthValidator,
    ActiveModel::Validations::PresenceValidator
  ], validators.map(&:class).sort_by(&:to_s)
end
test_list_of_validators_will_be_empty_when_empty() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 324
def test_list_of_validators_will_be_empty_when_empty
  Topic.validates :title, length: { minimum: 10 }
  assert_equal [], Topic.validators_on(:author_name)
end
test_multiple_errors_per_attr_iteration_with_full_error_composition() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 62
def test_multiple_errors_per_attr_iteration_with_full_error_composition
  r = Reply.new
  r.title   = ""
  r.content = ""
  r.valid?

  errors = r.errors.to_a

  assert_equal "Content is Empty", errors[0]
  assert_equal "Title is Empty", errors[1]
  assert_equal 2, r.errors.count
end
test_numericality_validation_checks_against_raw_value() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 172
def test_numericality_validation_checks_against_raw_value
  klass = Class.new(Topic) do
    def self.model_name
      ActiveModel::Name.new(self, nil, "Topic")
    end
    attribute :wibble, :decimal, scale: 2, precision: 9
    validates_numericality_of :wibble, greater_than_or_equal_to: BigDecimal.new("97.18")
  end

  assert_not klass.new(wibble: "97.179").valid?
  assert_not klass.new(wibble: 97.179).valid?
  assert_not klass.new(wibble: BigDecimal.new("97.179")).valid?
end
test_numericality_validation_with_mutation() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 160
def test_numericality_validation_with_mutation
  klass = Class.new(Topic) do
    attribute :wibble, :string
    validates_numericality_of :wibble, only_integer: true
  end

  topic = klass.new(wibble: "123-4567")
  topic.wibble.gsub!("-", "")

  assert topic.valid?
end
test_save_without_validation() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 126
def test_save_without_validation
  reply = WrongReply.new
  assert !reply.save
  assert reply.save(validate: false)
end
test_single_attr_validation_and_error_msg() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 30
def test_single_attr_validation_and_error_msg
  r = Reply.new
  r.title = "There's no content!"
  assert r.invalid?
  assert r.errors[:content].any?, "A reply without content should mark that attribute as invalid"
  assert_equal ["is Empty"], r.errors["content"], "A reply without content should contain an error"
  assert_equal 1, r.errors.count
end
test_single_error_per_attr_iteration() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 52
def test_single_error_per_attr_iteration
  r = Reply.new
  r.valid?

  errors = r.errors.collect { |attr, messages| [attr.to_s, messages] }

  assert_includes errors, ["title", "is Empty"]
  assert_includes errors, ["content", "is Empty"]
end
test_single_field_validation() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 19
def test_single_field_validation
  r = Reply.new
  r.title = "There's no content!"
  assert r.invalid?, "A reply without content should be invalid"
  assert r.after_validation_performed, "after_validation callback should be called"

  r.content = "Messa content!"
  assert r.valid?, "A reply with content should be valid"
  assert r.after_validation_performed, "after_validation callback should be called"
end
test_strict_validation_custom_exception() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 401
def test_strict_validation_custom_exception
  Topic.validates_presence_of :title, strict: CustomStrictValidationException
  assert_raises CustomStrictValidationException do
    Topic.new.valid?
  end
end
test_strict_validation_error_message() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 420
def test_strict_validation_error_message
  Topic.validates :title, strict: true, presence: true

  exception = assert_raises(ActiveModel::StrictValidationFailed) do
    Topic.new.valid?
  end
  assert_equal "Title can't be blank", exception.message
end
test_strict_validation_in_custom_validator_helper() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 394
def test_strict_validation_in_custom_validator_helper
  Topic.validates_presence_of :title, strict: true
  assert_raises ActiveModel::StrictValidationFailed do
    Topic.new.valid?
  end
end
test_strict_validation_in_validates() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 375
def test_strict_validation_in_validates
  Topic.validates :title, strict: true, presence: true
  assert_raises ActiveModel::StrictValidationFailed do
    Topic.new.valid?
  end
end
test_strict_validation_not_fails() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 382
def test_strict_validation_not_fails
  Topic.validates :title, strict: true, presence: true
  assert Topic.new(title: "hello").valid?
end
test_strict_validation_particular_validator() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 387
def test_strict_validation_particular_validator
  Topic.validates :title, presence: { strict: true }
  assert_raises ActiveModel::StrictValidationFailed do
    Topic.new.valid?
  end
end
test_throw_away_typing() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 140
def test_throw_away_typing
  d = Developer.new("name" => "David", "salary" => "100,000")
  assert !d.valid?
  assert_equal 100, d.salary
  assert_equal "100,000", d.salary_before_type_cast
end
test_valid_uses_create_context_when_new() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 19
def test_valid_uses_create_context_when_new
  r = WrongReply.new
  r.title = "Wrong Create"
  assert_not r.valid?
  assert r.errors[:title].any?, "A reply with a bad title should mark that attribute as invalid"
  assert_equal ["is Wrong Create"], r.errors[:title], "A reply with a bad content should contain an error"
end
test_valid_uses_update_context_when_persisted() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 27
def test_valid_uses_update_context_when_persisted
  r = WrongReply.new
  r.title = "Bad"
  r.content = "Good"
  assert r.save, "First validation should be successful"

  r.title = "Wrong Update"
  assert_not r.valid?, "Second validation should fail"

  assert r.errors[:title].any?, "A reply with a bad title should mark that attribute as invalid"
  assert_equal ["is Wrong Update"], r.errors[:title], "A reply with a bad content should contain an error"
end
test_valid_using_special_context() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 40
def test_valid_using_special_context
  r = WrongReply.new(title: "Valid title")
  assert !r.valid?(:special_case)
  assert_equal "Invalid", r.errors[:author_name].join

  r.author_name = "secret"
  r.content = "Good"
  assert r.valid?(:special_case)

  r.author_name = nil
  assert_not r.valid?(:special_case)
  assert_equal "Invalid", r.errors[:author_name].join

  r.author_name = "secret"
  assert r.valid?(:special_case)
end
test_validate() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 343
def test_validate
  Topic.validate do
    validates_presence_of :title, :author_name
    validates_length_of :content, minimum: 10
  end

  topic = Topic.new
  assert_empty topic.errors

  topic.validate
  assert_not_empty topic.errors
end
test_validate_block() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 146
def test_validate_block
  Topic.validate { errors.add("title", "will never be valid") }
  t = Topic.new("title" => "Title", "content" => "whatever")
  assert t.invalid?
  assert t.errors[:title].any?
  assert_equal ["will never be valid"], t.errors["title"]
end
test_validate_block_with_params() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 154
def test_validate_block_with_params
  Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
  t = Topic.new("title" => "Title", "content" => "whatever")
  assert t.invalid?
  assert t.errors[:title].any?
  assert_equal ["will never be valid"], t.errors["title"]
end
test_validate_with_bang() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 356
def test_validate_with_bang
  Topic.validates :title, presence: true

  assert_raise(ActiveModel::ValidationError) do
    Topic.new.validate!
  end
end
test_validate_with_bang_and_context() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 364
def test_validate_with_bang_and_context
  Topic.validates :title, presence: true, on: :context

  assert_raise(ActiveModel::ValidationError) do
    Topic.new.validate!(:context)
  end

  t = Topic.new(title: "Valid title")
  assert t.validate!(:context)
end
test_validates_acceptance_of_as_database_column() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 147
def test_validates_acceptance_of_as_database_column
  Topic.validates_acceptance_of(:approved)
  topic = Topic.create("approved" => true)
  assert topic["approved"]
end
test_validates_acceptance_of_with_non_existent_table() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 132
def test_validates_acceptance_of_with_non_existent_table
  Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base)

  assert_nothing_raised do
    IncorporealModel.validates_acceptance_of(:incorporeal_column)
  end
end
test_validates_each() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 118
def test_validates_each
  hits = 0
  Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
    record.errors.add attr, "gotcha"
    hits += 1
  end
  t = Topic.new("title" => "valid", "content" => "whatever")
  assert t.invalid?
  assert_equal 4, hits
  assert_equal %w(gotcha gotcha), t.errors[:title]
  assert_equal %w(gotcha gotcha), t.errors[:content]
end
test_validates_each_custom_reader() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 131
def test_validates_each_custom_reader
  hits = 0
  CustomReader.validates_each(:title, :content, [:title, :content]) do |record, attr|
    record.errors.add attr, "gotcha"
    hits += 1
  end
  t = CustomReader.new("title" => "valid", "content" => "whatever")
  assert t.invalid?
  assert_equal 4, hits
  assert_equal %w(gotcha gotcha), t.errors[:title]
  assert_equal %w(gotcha gotcha), t.errors[:content]
ensure
  CustomReader.clear_validators!
end
test_validates_with_bang() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 408
def test_validates_with_bang
  Topic.validates! :title, presence: true
  assert_raises ActiveModel::StrictValidationFailed do
    Topic.new.valid?
  end
end
test_validates_with_false_hash_value() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 415
def test_validates_with_false_hash_value
  Topic.validates :title, presence: false
  assert Topic.new.valid?
end
test_validation_order() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 230
def test_validation_order
  Topic.validates_presence_of :title
  Topic.validates_length_of :title, minimum: 2

  t = Topic.new("title" => "")
  assert t.invalid?
  assert_equal "can't be blank", t.errors["title"].first
  Topic.validates_presence_of :title, :author_name
  Topic.validate { errors.add("author_email_address", "will never be valid") }
  Topic.validates_length_of :title, :content, minimum: 2

  t = Topic.new title: ""
  assert t.invalid?

  assert_equal :title, key = t.errors.keys[0]
  assert_equal "can't be blank", t.errors[key][0]
  assert_equal "is too short (minimum is 2 characters)", t.errors[key][1]
  assert_equal :author_name, key = t.errors.keys[1]
  assert_equal "can't be blank", t.errors[key][0]
  assert_equal :author_email_address, key = t.errors.keys[2]
  assert_equal "will never be valid", t.errors[key][0]
  assert_equal :content, key = t.errors.keys[3]
  assert_equal "is too short (minimum is 2 characters)", t.errors[key][0]
end
test_validation_with_if_and_on() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 255
def test_validation_with_if_and_on
  Topic.validates_presence_of :title, if: Proc.new { |x| x.author_name = "bad"; true }, on: :update

  t = Topic.new(title: "")

  # If block should not fire
  assert t.valid?
  assert t.author_name.nil?

  # If block should fire
  assert t.invalid?(:update)
  assert t.author_name == "bad"
end
test_validation_with_message_as_proc() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 280
def test_validation_with_message_as_proc
  Topic.validates_presence_of(:title, message: proc { "no blanks here".upcase })

  t = Topic.new
  assert t.invalid?
  assert_equal ["NO BLANKS HERE"], t.errors[:title]
end
test_validation_with_message_as_proc_that_takes_a_record_as_a_parameter() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 450
def test_validation_with_message_as_proc_that_takes_a_record_as_a_parameter
  Topic.validates_presence_of(:title, message: proc { |record| "You have failed me for the last time, #{record.author_name}." })

  t = Topic.new(author_name: "Admiral")
  assert t.invalid?
  assert_equal ["You have failed me for the last time, Admiral."], t.errors[:title]
end
test_validation_with_message_as_proc_that_takes_record_and_data_as_a_parameters() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 458
def test_validation_with_message_as_proc_that_takes_record_and_data_as_a_parameters
  Topic.validates_presence_of(:title, message: proc { |record, data| "#{data[:attribute]} is missing. You have failed me for the last time, #{record.author_name}." })

  t = Topic.new(author_name: "Admiral")
  assert t.invalid?
  assert_equal ["Title is missing. You have failed me for the last time, Admiral."], t.errors[:title]
end
test_validations_on_the_instance_level() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 329
def test_validations_on_the_instance_level
  Topic.validates :title, :author_name, presence: true
  Topic.validates :content, length: { minimum: 10 }

  topic = Topic.new
  assert topic.invalid?
  assert_equal 3, topic.errors.size

  topic.title = "Some Title"
  topic.author_name = "Some Author"
  topic.content = "Some Content Whose Length is more than 10."
  assert topic.valid?
end
test_validators() click to toggle source
# File activerecord/test/cases/validations_test.rb, line 153
def test_validators
  assert_equal 1, Parrot.validators.size
  assert_equal 1, Company.validators.size
  assert_equal 1, Parrot.validators_on(:name).size
  assert_equal 1, Company.validators_on(:name).size
end
validator_a() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 189
def validator_a
  @call_sequence << :a
end
validator_b() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 193
def validator_b
  @call_sequence << :b
end
validator_c() click to toggle source
# File activemodel/test/cases/validations_test.rb, line 197
def validator_c
  @call_sequence << :c
end