Testing RSpec model validations

I recently came across an issue while trying to write RSpec tests for model validations that I think are worth addressing. I think it’s worth testing validations because they may change over time and you want to make sure your test suite is accounting for them. For example, I have character limits for messages, which is a critical model validation. If I were to accidentally delete the validation or change the character limit, there would be a lot of unexpected consequences.

The first validation I wanted to test was a character limit on messages that was only triggered if the message was sent by a user. To do so, I created a from_user method that checks to see if the message.sender_type == 'user'. The model looks like this:

class Message < ActiveRecord::Base
  validates_presence_of :body, :status, :contact
  validates :body, length: { in: 1..420 }, if: :from_user?, on: :create

  def from_user?
    self.sender_type == 'user'
  end
end

At first, it seemed like I could easily use thoughtbot’s shoulda gem to write a simple test.

describe 'character limit for user-sent messages' do
  it {should_validate_length_of(:body).is_at_least(1).is_at_most(420).on(:create) }
end

However, the validation kept failing. After a little bit of digging, I realized it was because I never specified a subject. In particular, I never specified that my subject was a user-sent message. I found that there are two solutions to this problem.

The first is to specify a subject with a FactoryGirl.build. It’s important that you use ‘build’ and not ‘create’, because the ‘create’ method will trigger all model validations, which is exactly what you’re trying to test. In addition, when you ‘build’ the model, make sure you leave out the attribute that you’re testing. In my case, I’m trying to test a body validation, so it wouldn’t make sense to do a FactoryGirl.build where I specify the message body.

The second solution is to write a ‘before’ hook that lets the test assume that the subject is from a user, which will trigger the validation. Either method works well, but I opted to use the second option to practice a new technique.

Option 1

describe 'character limit for user-sent messages' do
  subject { FactoryGirl.build :message, contact: contact, status: 'sent' }
  it {should_validate_length_of(:body).is_at_least(1).is_at_most(420).on(:create) }
end

Option 2

describe 'character limit for user-sent messages' do
  before { allow(subject).to receive(:from_user?).and_return(true) }
  it {should_validate_length_of(:body).is_at_least(1).is_at_most(420).on(:create) }
end
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s