Validations should be covered by a model's spec.
This card shows how to test an individual validation. This is preferrable to save an entire record and see whether it is invalid.
Recipe for testing any validation
In general any validation test for an attribute :attribute_being_tested looks like this:
- Make a model instance (named recordbelow)
- Run validations by saying record.validate
- Check if record.errors[:attribute_being_tested]contains the expected validation error
- Put the attribute into a valid state
- Run validations again by saying record.validate
- Check if record.errors[:attribute_being_tested]does not contain the expected validation error
For instance, we want to test that User#email is present:
describe User do
  describe '#email' do
    it 'validates presence' do
      record = User.new
      record.email = '' # invalid state
      record.validate 
      expect(record.errors[:email]).to include("can't be blank") # check for presence of error
      record.email = 'foo@bar.com' # valid state
      record.validate 
      expect(record.errors[:email]).to_not include("can't be blank") # check for absence of error
    end
  end
  
end
Standard validations can be tested with less code
The 
  shoulda-matchers
  
    Show archive.org snapshot
  
 gem gives you some RSpec matchers to test the application of standard Rails validations. Under the hood should-matchers uses the same recipe as outlined above (set invalid state, run validations, check for message, etc.), but your tests will have a lot less code:
    describe User do
      describe '#screen_name' do
        it { is_expected.to validate_presence_of(:screen_name) }
      end
      describe '#email' do
        it { is_expected.not_to allow_value("blah").for(:email) }
        it { is_expected.to allow_value("a@b.com").for(:email) }
      end
      describe '#age' do
        it { is_expected.to validate_inclusion_of(:age).in_range(1..100) }
      end
    end
See the 
  shoulda-matchers README
  
    Show archive.org snapshot
  
 for a full list of matchers provided. See our dedicated card for should validate_uniqueness_of, it works differently than you think.
Where to put validation examples
We usually sort our specs by method name.
While a case can be made by putting validation tests under describe '#validate', I prefer to sort validation tests under the example group for the method that is being validated. This way they will be right next to defaults and other behavior for that method:
describe Report do
  describe '#currency' do
    it { is_expected.to allow_values('EUR', 'USD', 'GBP', 'CHF').for(:currency) }
    it { is_expected.not_to allow_values('foo', '€', '', nil).for(:currency) }
    it 'defaults to EUR' do
      expect(subject.currency).to eq('EUR')
    end
  end
  describe '#year' do
    it { is_expected.to validate_presence_of(:year) }
    it 'defaults to the current year' do
      expect(subject.year).to eq(Date.today.year)
    end
  end
end
Testing custom validation methods
The recipe above applies to custom validation methods as well.
Let's say you want to test that User#screen_name is not a 
  palindrome
  
    Show archive.org snapshot
  
. Since that check is not possible with standard Rails validations, we write a custom validation method like this:
class User < ActiveRecord::Base
  validate :validate_screen_name_is_no_palindrome
  private
  def validate_screen_name_is_no_palindrome
    if screen_name.downcase == screen_name.downcase.reverse
      errors.add(:screen_name, 'must not be a palindrome')
    end
  end
end
We can now reply the test recipe from above:
describe User do
  describe '#screen_name' do
    it "validates that it's not a palindrome" do
      subject.screen_name = 'Hannah'
      subject.validate
      expect(subject.errors[:screen_name]).to include('must not be a palindrome')
      subject.screen_name = 'Johanna'
      subject.validate
      expect(subject.errors[:screen_name]).to_not include('must not be a palindrome')
    end
  end
end
Different ways of testing errors
Checking the human errors messages
subject.validate
expect(subject.errors(:bar)).to contain_exactly('some error')
subject.validate
expect(subject.errors(:bar)).to eq(['some error'])
Checking the error message key
subject.validate
expect(subject.errors).to be_of_kind(:bar, :invalid)