I'm trying to create a test case for User model. Basically, it will validate first_name and last_name to be present.
What I am trying to do is to check whether the error on a specific field is empty or not and it should be empty. However it always fails.
What is the correct way to do this?
Here is my code
On my user_spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new
end
it "must have a first name" do
#user.errors[:first_name].should_not be_empty
end
it "must have a last name" do
#user.errors[:last_name].should_not be_empty
end
end
On my user.rb file
class User < ActiveRecord::Base
validates :first_name, :presence => true
validates :last_name, :presence => true
end
You can test by simply doing this as well:
describe 'validations' do
it { should validate_presence_of :firstname }
it { should validate_presence_of :lastname }
end
Take a look at the shoulda matchers for all such standard Rails Validation.
This way is not just more concise but also takes care of the positive case. Meaning you then dont need to test the scenario mentioned below:
it "passed validations when first_name is set"
user = User.create(:firstname => 'f', :lastname => 'l')
user.errors[:first_name].should be_empty
user.errors[:last_name].should be_empty
end
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
Related
I'm trying to create a test to check if my post's embedded document(author) call to it callback method.
Code:
class Post
include Mongoid::Document
include Mongoid::Timestamps::Created
include Mongoid::Timestamps::Updated
{....}
# relations
embeds_one :author, cascade_callbacks: true
accepts_nested_attributes_for :author
{...}
end
Class Author
include Mongoid::Document
include Mongoid::Timestamps::Created
include Mongoid::Timestamps::Updated
{...}
embedded_in :post
after_save :my_callback_method
def save_estimation_logs
{...}
end
{...}
end
test:
RSpec.describe Author, :type => :model do
context "Create author on Post" do
let!(:post) { create(:post, :with_external_author) }
it "should call after_save method my_callback_method when saving" do
expect(post.author).to receive(:my_callback_method)
expect(post.save).to eq true
end
end
end
when i'm trying to run this rspec - i got
Failure/Error: expect(post.author).to receive(:my_callback_method)
(#<Author _id: 5c7ea762f325709edac2ae84, created_at: 2019-03-05 16:44:18 UTC, updated_at: 2019-03-05 16:44:18 UTC>). my_callback_method(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
Can you guys help me understand how should I test this embedded document callbacks?
First of all, you should trust mongoid to call after_save and test my_callback_method in isolation.
Now, as said in the comments, that you want to check if someone deleted the after_save, you can add a test for:
RSpec.describe Author, :type => :model do
context "Author" do
it "should define my_callback_method for after_save" do
result = Author._save_callbacks.select { |cb| cb.kind.eql?(:after) }.collect(&:filter).include?(:my_callback_method)
expect(result).to eq true
end
end
end
Your code looks correct but there is a number of outstanding issues in Mongoid related to callbacks in persistence. Ensure the callback is called in normal operation (i.e. when you save a post from a Rails console).
I've written some RSpec tests that successfully create objects with :let statements. However, the test environment doesn't maintain the associations that function properly everywhere else. Below is an example of a class that would turn up a NoMethodError (undefined method `money' for nil:NilClass). Money is a column in Inventory. Any thoughts?
class Inventory < ActiveRecord::Base
belongs_to :character
def self.return_money(character)
character.inventory.money
end
end
And here's a corresponding example for a spec doc:
require 'spec_helper'
describe 'Test methods' do
let(:trader) {
Character.create(
name: "Trader",
location_id: 1)
}
let(:trader_inventory) {
Inventory.create(
character_id: trader.id,
storage_capacity: 50000,
money: 20000,
markup: 1.35)
}
it "test method" do
expect(Inventory.return_money(trader)).to eq(100)
end
end
There is no reason this shouldn't work. RSpec isn't special, it's just regular Ruby code. You can confirm this by moving all of your code into a single file, something like:
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)
class Inventory < ActiveRecord::Base
end
class Character < ActiveRecord::Base
has_one :inventory
end
describe 'test' do
it 'works' do
puts Character.first.inventory.money.inspect
end
end
Guesses as to what may be broken:
money is a composite field or something like that. Can you post your database schema?
Library files aren't being loaded correctly. Use puts $LOADED_FEATURES to verify that all the files that should be required have been.
I have a several models and in this models I have attributes that I don't want to be blank / empty.
I want to heavily test my models on these restrictions using RSpec and Factory Girl.
However I end up with code duplication:
user_spec:
it 'is invalid if blank' do
expect {
FactoryGirl.create(:user, nickname => '')
}.to raise_error(ActiveRecord::RecordInvalid)
end
message_spec:
it 'is invalid if blank' do
expect {
FactoryGirl.create(:message, :re => '')
}.to raise_error(ActiveRecord::RecordInvalid)
end
How can I factor it ?
RSpec provides several ways to do so, such as Shared Examples.
1. Create a file in your [RAILS_APP_ROOT]/support/
Based on your example, you could name this file not_blank_attribute.rb. Then, you just have to move your duplicated code in and adapting it to make it configurable :
RSpec.shared_examples 'a mandatory attribute' do |model, attribute|
it 'should not be empty' do
expect {
FactoryGirl.create(model, attribute => '')
}.to raise_error(ActiveRecord::RecordInvalid)
end
end
2. Use it_behaves_like function in your specs
This function will call the shared example.
RSpec.describe User, '#nickname' do
it_behaves_like 'a mandatory attribute', :User, :nickname
end
Finally, it outputs:
User#nickname
behaves like a mandatory attribute
should not be empty
I am so confused as to why this is happening...
I'm using factory girl, and this factory:
# spec/factories/partners/contact.rb
FactoryGirl.define do
factory :partner_contact, class: Partners::Contact do
first_name "Josh"
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
partner
end
end
But when I run my rspec it says
Mysql2::Error: Field 'first_name' doesn't have a default value: INSERT INTO `partner_contact` (`created_on`) VALUES ('2014-01-30 22:21:53')
Here is the spec that I'm using that generates the above error.
# spec/models/contact.rb
require 'spec_helper'
require 'pp'
describe Partners::Contact do
it "has a valid factory" do
partner = FactoryGirl.create(:partner_contact)
# partner.should be_valid
puts "MEOW MEOW"
end
it "is invalid without a firstname" do
# FactoryGirl.build(:partner_contact, first_name: nil).should_not be_valid
end
it "is invalid without a lastname" do
# FactoryGirl.build(:partner_contact, last_name: nil).should_not be_valid
end
it "is invalid without an email address" do
# FactoryGirl.build(:partner_contact, email: nil).should_not be_valid
end
#it "returns a contact's fullname as a string"
end
As you can see, it's not trying to commit any data that I listed, first_name, last_name or email. Just 'created_on' which is generated via rails.
What's going on here?
Do you have any fixtures for that table? If fixtures are present and enabled, Rails will attempt to load them into your database before your test is evaluated. Look in spec/fixtures.
Your setup seems wrong. Check the FactoryGirl Rails gem. Don't forget to configure RSpec to use FactoryGirl as well.
I have implemented validations in a dependent manner, like if start_date format is invalid so i don't want to run other validation on start_date.
validates_format_of :available_start_date, :with => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}((((\-|\+){1}\d{2}:\d{2}){1})|(z{1}|Z{1}))$/, :message => "must be in the following format: 2011-08-25T00:00:00-04:00"
This checks for a specific format and then i have custom validation methods called from which should run later.
def validate
super
check_offer_dates
end
I have used self.errors["start_date"] to check if the error object contains errors, it should skip the other validations on same parameter if it's not empty.
But the problem is def validate is called first and then the validates_format_of. How can i change this so that the flow can be achieved.
I just ran into a similar problem; this is how I fixed it using a before_save callout:
Not working (validates in wrong order - I want custom validation last):
class Entry < ActiveRecord::Base
validates_uniqueness_of :event_id, :within => :student_id
validate :validate_max_entries_for_discipline
def validate_max_entries_for_discipline
# set validation_failed based on my criteria - you'd have your regex test here
if validation_failed
errors.add(:maximum_entries, "Too many entries here")
end
end
end
Working (using before_save callout):
class Entry < ActiveRecord::Base
before_save :validate_max_entries_for_discipline!
validates_uniqueness_of :event_id, :within => :student_id
def validate_max_entries_for_discipline!
# set validation_failed based on my criteria - you'd have your regex test here
if validation_failed
errors.add(:maximum_entries, "Too many entries here")
return false
end
end
end
Note the changes:
validate_max_entries_for_discipline becomes validate_max_entries_for_discipline!
validation method now returns false on failure
validate validate_max_entries_for_discipline becomes before_save validate_max_entries_for_discipline!