Rails 3.1 Rspec Creating Test Case Validate Field for Model

Rails 3.1 Rspec Creating test case validate field for Model

RSpec supports the notion of an "implicit" subject. If your first argument to the "describe" block is a class, RSpec automatically makes an instance of that class available to your specs. See http://relishapp.com/rspec/rspec-core/v/2-6/dir/subject/implicit-subject.

require 'spec_helper'

describe User do

it "must have a first name" do
subject.should have(1).error_on(:first_name)
end

it "must have a last name" do
subject.should have(1).error_on(:last_name)
end
end

which results in RSpec output (if using --format documentation) of:

User
must have a first name
must have a last name

You can abbreviate it even further if you are content with the RSpec output defaults:

require 'spec_helper'

describe User do
it { should have(1).error_on(:first_name) }
it { should have(1).error_on(:last_name) }
end

which results in:

User
should have 1 error on :first_name
should have 1 error on :last_name

Rails 3.1, RSpec: testing model validations

CONGRATULATIONS on you endeavor into TDD with ROR I promise once you get going you will not look back.

The simplest quick and dirty solution will be to generate a new valid model before each of your tests like this:

 before(:each) do
@user = User.new
@user.username = "a valid username"
end

BUT what I suggest is you set up factories for all your models that will generate a valid model for you automatically and then you can muddle with individual attributes and see if your validation. I like to use FactoryGirl for this:

Basically once you get set up your test would look something like this:

it "should have valid factory" do
FactoryGirl.build(:user).should be_valid
end

it "should require a username" do
FactoryGirl.build(:user, :username => "").should_not be_valid
end

Here is a good railscast that explains it all better than me:


UPDATE: As of version 3.0 the syntax for factory girl has changed. I have amended my sample code to reflect this.

Testing Rails model validations with RSpec, without testing AR itself

The functionalities of:

  • Rails being able to validate the presence of an arbitrary value on your model
  • errors being added to an object for an attribute that is missing when a validation for it is configured

are covered in the tests for Rails itself (specifically, in the ActiveModel tests).

That leaves needing to write the tests for the config that covers the business logic of your app eg validating the presence of the specific name attribute on your specific User class etc. In my opinion, the matchers from the shoulda-matchers gem should have you covered:

RSpec.describe User, type: :model do
subject(:user) { build(:user) } # assuming you're using FactoryGirl

describe 'validations' do
specify 'for name' do
expect(user).to validate_presence_of(:name).on(:create)
# NOTE: saving here is needed to test uniqueness amongst users in
# the database
user.save
expect(user).to validate_uniqueness_of(:name)
end

specify 'for password' do
expect(user).to validate_presence_of(:password)
expect(user).to allow_value('abcd').for(:password)
expect(user).to_not allow_value('1234').for(:password)
end
end
end

I think that unless you have specific custom error messages for your errors that you want to test for (ie you've overridden the default Rails ones), then tests like expect(user.errors[:name]).to be_present can be removed (even if you have custom errors, I still think they're of dubious value since those messages will become locale-dependent if you internationalise your app, so I'd test for the display of some kind of error on the page in a feature spec instead).

I can write so many tests for the regex, but it will be hell for maintenance.

I don't think you can really get around this when testing validations for format, so I'd suggest just write some representative test cases and then add/remove those cases as you discover any issues you may have missed, for example:

# use a `let` or extract out into a test helper method
let(:valid_passwords) do
['abcd', 'ABCD', 'AbCd'] # etc etc
end

describe 'validations' do
specify 'for password' do
valid_passwords.each do |password|
expect(user).to allow_value(password).for(:password)
end
end
end

How much you think will be full test coverage in this example?

I've gotten 100% code coverage from reports like SimpleCov when writing unit specs as described above.

How do I test associated models with Rspec

It seems prudent to have test code parallel app code as closely as possible. That is, if UserApplication will be created via User in the controller, it ought to be done the same way in the test. Furthermore, your UserApplication validations will probably test the association sooner or later anyway, so the test subject should be created in such a way as to be valid. With that in mind, you can set up your tests as follows:

require 'spec_helper'

describe UserApplication do
let(:user) { User.create(user_params) }
before { @user_application = user.user_applications.build(name: 'name') }

subject { @user_application }

describe 'validations' do
context 'when name is missing' do
before { @user_application.name = '' }
it { should_not be_valid }
end

context 'when user_id is missing' do
before { @user_application.user_id = nil }
it { should_not be_valid }
end

# other validations
end
end

How do I test my devise user model validations using RSpec?

It seems to be fine, may be, my testing code helps you out:

user_spec.rb

require 'spec_helper'

describe User do
before :each do
@user = Factory.build(:user)
end

it "should not be valid without a first_name" do
@user.first_name = nil
@user.should_not be_valid
end

end

user.rb (Model)

class User < ActiveRecord::Base

validates_presence_of :first_name

# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :registerable, :lockable,
:recoverable, :rememberable, :trackable
# Setup accessible (or protected) attributes for your model
attr_accessible :login, :first_name, :email, :password, :password_confirmation, :remember_me
attr_accessor :login

devise :database_authenticatable, :recoverable, :validatable

protected

def password_required?
!persisted? || password.present? || password_confirmation.present?
end

end

rails rspec testing updating attribute on model

You just need to reload your object:

expect(reminder.reload.verified).to eq(true)

Rails :less_than validation makes RSpec test fail

I think I might have reached a good solution that isn't just a workaround that doesn't actually solve the original issue I was having with the test.

Running the following while in rails c throws a validation error, as expected:

new = Part.create(available_quantity: 0, minimum_order: 10)
new.save # => false
new.errors.full_messages # => ["Quantity available must be greater than 0", "Minimum order must be less than or equal to 0"]

As you can see, the 2 validations work exactly as expected, but the issue in the test is that one validation is actually getting in the way of the other, which obviously causes issues with the passing of the test.

At this point, there are two options that I saw in order to proceed:

  1. Make this one specific test also have a nil value for the minimum_order field.

This causes some minor code repetition and just overall wasn't good enough for my tastes


  1. Simply skip the validation if the current value of the quantity_available field is nil or 0.

This one makes a lot more sense. After all, the 1st validation is going to make sure a 0 never makes it into the DB for either of these fields, so it's safe to simply skip the 2nd validation if a 0 or nil is present. If it's not, the validation will fire and only then will it compare the two values.

The solution, then, is pretty simple; Just stick an unless: into the validation that evaluates a lambda

validates_numericality_of :minimum_order, less_than: :quantity_available, unless: -> { quantity_available.nil? || quantity_available == 0 }

With that, all Tests are green, no workarounds were needed, and I'm pretty sure my Model is the tiniest bit more efficient in its validation checks



Related Topics



Leave a reply



Submit