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:
- 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
- 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
How to Get Ruby-Debug-Ide to Work
How to Declare a Rake Task That Depends on a Parameterized Task
How to Implement Composite Primary Keys in Rails
Find Out Which Gems Require Native C Extensions from a Gemfile
I Can't Remove Whitespaces from a String Parsed by Nokogiri
How to Host a Rails Web Application
Invoking Knife in a Ruby Class
Ruby Array Concat Versus + Speed
E: Unable to Locate Package Heroku-Toolbelt
Ruby: Multiply All Elements of an Array
How to Properly Test Cancan Abilities with Rspec
Best Way to Remove File Extension
How to Redirect Back to a Page I'M Currently On
What's the Efficient Way to Multiply Two Arrays and Get Sum of Multiplied Values in Ruby
How to Use Ruby to Write Individual Records to a Redshift Database