RSpec - how to test default value for attribute using callback - ruby-on-rails-3

Here's my test:
require 'spec_helper'
describe League do
it 'should default weekly to false' do
league = Factory.create(:league, :weekly => nil)
league.weekly.should == false
end
end
end
And here's my model:
class League < ActiveRecord::Base
validates :weekly, :inclusion => { :in => [true, false] }
before_create :default_values
protected
def default_values
self.weekly ||= false
end
end
When I run my test, I get the following error message:
Failure/Error: league = Factory.create(:league, :weekly => nil)
ActiveRecord::RecordInvalid:
Validation failed: Weekly is not included in the list
I've tried a couple different approaches to trying to create a league record and trigger the callback, but I haven't had any luck. Is there something that I am missing about testing callbacks using RSpec?

I believe that what you are saying is, before create, set weekly to false, then create actually sets weekly to nil, overwriting the false.
Just do
require 'spec_helper'
describe League do
it 'should default weekly to false' do
league = Factory.create(:league) # <= this line changed
league.weekly.should == false
end
end
end
in your test. No need to explicitly set nil.

Related

Not able to overwrite object properties in "create" method

I have a simple table for storing users accounts information (emails and passwords) with two additional columns:
is_active - says if user account is enable or disabled - the column
type is boolean and in the context of DB2 it is mapped with
decimal(1)
registration_date - says when the user was created - the column
type is datetime and in the context ofDB2 it is mapped with
datetime
As this fields will not be set by the user, I have deleted their inputs from the users _form.
I want to populated this fields in my users controller as follows:
def create
#security_user = SecurityUser.new(params[:security_user])
#security_user.is_active = 0
#security_user.registration_date = DateTime.now
...
end
But I can not pass the validations that I have in the model. They looks like:
class SecurityUser < ActiveRecord::Base
# Loading custom validators
require 'lib/date_format_validator'
...
# Accessible columns
...
# Relationships
...
# Validations
validates :is_active, inclusion: { in: 0..1, message: "only '0' and '1' allowed"}, presence: true
validates :registration_date, date_format:true , presence: true
end
where the 'lib/date_format_validator' looks like follows:
class DateFormatValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
if (DateTime.parse(value) rescue ArgumentError) == ArgumentError
object.errors[attribute] << (options[:message] || "is not valid datetime")
end
end
end
What I am doing wrong?
EDIT: The screenshot below displays the errors:
EDIT2: Sam Ruby's answer helps me to finish with something like this for my custom validator method:
class DateFormatValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless value.kind_of? DateTime || (DateTime.parse(value) rescue ArgumentError) != ArgumentError
object.errors[attribute] << (options[:message] || "is not valid datetime")
end
end
end
and to transform the validates method for the is_active column as follows:
validates :is_active, inclusion: { in: [ true, false ], message: "only 'true' and 'false' allowed"}
because as it is said in the official documentation:
Since false.blank? is true, if you want to validate the presence of a boolean field you should use validates :field_name, :inclusion => { :in => [true, false] }.
The problem is that you are trying to validate the ActiveRecord object as if the columns are of type String. But since you have defined your columns as boolean and datetime, what you will be validating will be of type TrueClass, FalseClass or ActiveSupport::TimeWithZone.
In other words, the values are already parsed.
true is never 0 or 1.
DateTime.parse(DateTime.now) will always raise ArgumentError
If you want to validate the unparsed values, do so in the controller.

rails 3 validations uniqueness on attributes of an association

i have a model called Fund and a model called Company .. where fund belongs_to a company.
i have this validation in my Fund table:
validates :name, presence: true, uniqueness: true
This works both on server side and client side using client_side_validations. But i want my fund names to be unique across both fund.name values and fund.company.name values. And i want to do it in a way it would work with client_side_validations too.
Suggestions?
Ended up creating a very specific validator and adding it to client-side-validation. Here'z the breakdown
In models/fund.rb
validates_fund_name_not_company_name :name
new file in config/initializers/validators .. called fund_name_not_company_name_validator.rb
class FundNameNotCompanyNameValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
if ::Company.exists?(name: value)
record.errors.add(attr_name, :fund_name_not_company_name, options.merge(:value => value))
end
end
end
# This allows us to assign the validator in the model
module ActiveModel::Validations::HelperMethods
def validates_fund_name_not_company_name(*attr_names)
validates_with FundNameNotCompanyNameValidator, _merge_attributes(attr_names)
end
end
module ClientSideValidations::Middleware
class FundNameNotCompanyName < ClientSideValidations::Middleware::Base
def response
if ::Company.exists?(name: request.params[:name])
self.status = 404
else
self.status = 200
end
super
end
end
end
then in app/assets/javascripts/rails.validations.custom.js
clientSideValidations.validators.remote['fund_name_not_company_name'] = function(element, options) {
if ($.ajax({
url: '/validators/fund_name_not_company_name',
data: { name: element.val() },
// async must be false
async: false
}).status == 404) { return options.message; }
}
This helped a great deal

Destroy method failing in controller test

I'm experiencing a bizarre issue testing a destroy method. I'm using FactoryGirl and Rspec.
Here's a look at the method in question. As you can see, it doesn't actually destroy the dealer, just set it and it's dependent object's active attributes to false:
dealers_controller.rb
def destroy
#dealer = Dealer.find(params[:id])
#dealer.active = false
#dealer.save!
#dealer.leads.each { |lead|
lead.active = false
lead.save!
}
#dealer.users.each { |user|
user.active = false
user.save!
}
redirect_to dealers_path
end
When I run this method in the application it does exactly what it should do. Now, on to the test.
dealers_controller_spec.rb
describe "#destroy" do
context "when deleting a valid record" do
let(:dealer) { FactoryGirl.create(:dealer_with_stuff) }
before do
#user = FactoryGirl.build(:admin_user)
login_user
delete :destroy, :id => dealer.id
end
it { should assign_to(:dealer).with(dealer) }
it { should redirect_to(dealers_path) }
it { should set_the_flash }
it "is no longer active" do
dealer.active.should be_false
end
it "has no active users" do
dealer.users.each do |user|
user.active.should be_false
end
end
it "has no active leads" do
dealer.leads.each do |lead|
lead.active.should be_false
end
end
end
end
The first 3 tests pass, but the last 3 all fail (weirdly, the user.active.should be_false test only fails if I put a sleep(10) after delete :destroy up above, but let's not get into that issue now). So when I check the test log, it goes through the entire destroy process, but then does a ROLLBACK, so for some reason it doesn't save any of the records; but it doesn't give me any more information than that.
Does anyone have any thoughts on this? I've tried everything I can possibly think of.
What if you reload the dealer? The dealer in your tests is different from the #dealer object in the controller (ActiveRecord doesn't do identity maps).
before do
#user = FactoryGirl.build(:admin_user)
login_user
delete :destroy, :id => dealer.id
dealer.reload # << add this
end

Testing a before_save callback with Rspec and Factory Girl

I am pretty sure I am missing something really basic here.
I want to test if a before_save callback does what it is supposed to do, not just that it is called.
I wrote the following test:
it 'should add lecture to question_type' do
#course = Factory :course,
:start_time => Time.now - 1.hour,
:end_time => Time.now
#question = Factory.create(:question,
:course_id => #course.id,
:created_at => Time.now - 10.minutes)
#question.question_type.should == 'lecture'
end
And I have the following factories for course and question:
Factory.define :course do |c|
c.dept_code {"HIST"}
c.course_code { Factory.next(:course_code) }
c.start_time { Time.now - 1.hour }
c.end_time { Time.now }
c.lecture_days { ["Monday", Time.now.strftime('%A'), "Friday"] }
end
Factory.define :question do |q|
q.content {"Why don't I understand this class!?"}
q.association :course
end
And I wrote the following callback in my Question model:
before_save :add_type_to_question
protected
def add_type_to_question
#course = Course.find(self.course_id)
now = Time.now
if (time_now > lecture_start_time && time_now < lecture_end_time ) && #course.lecture_days.map{|d| d.to_i}.include?(Time.now.wday)
self.question_type = "lecture"
end
end
The test keeps failing saying that "got: nil" for question_type instead of 'lecture'
Since I didn't see anything obviously wrong with my implementation code, I tried the callback in my development environment and it actually worked adding 'lecture' to question_type.
This makes me think that there might be something wrong with my test. What am I missing here? Does Factory.create skip callbacks by default?
I would not use Factory.create to trigger the process. FactoryGirl should be used to create the test setup, not to trigger the actual code you want to test. Your test would then look like:
it 'should add lecture to question_type' do
course = Factory(:course, :start_time => Time.now - 1.hour, :end_time => Time.now)
question = Factory.build(:question, :course_id => course.id, :created_at => Time.now - 10.minutes, :question_type => nil)
question.save!
question.reload.question_type.should == 'lecture'
end
If this test still fails, you can start debugging:
Add a puts statement inside add_type_to_question and another one inside the if statement and see what happens.

How can I map between strings and attributes automatically?

I have a tiny logical error in my code somewhere and I can't figure out exactly what the problem is. Let's start from the beginning. I have the following extension that my order class uses.
class ActiveRecord::Base
def self.has_statuses(*status_names)
validates :status,
:presence => true,
:inclusion => { :in => status_names}
status_names.each do |status_name|
scope "all_#{status_name}", where(status: status_name)
end
status_names.each do |status_name|
define_method "#{status_name}?" do
status == status_name
end
end
end
end
This works great for the queries and initial setting of "statuses".
require "#{Rails.root}/lib/active_record_extensions"
class Order < ActiveRecord::Base
has_statuses :created, :in_progress, :approved, :rejected, :shipped
after_initialize :init
attr_accessible :store_id, :user_id, :order_reference, :sales_person
private
def init
if new_record?
self.status = :created
end
end
end
Now I set a status initially and that works great. No problems at all and I can save my new order as expected. Updating the order on the other hand is not working. I get a message saying:
"Status is not included in the list"
When I check it seems that order.status == 'created' and it's trying to match against :created. I tried setting the has_statuses 'created', 'in_progress' etc but couldn't get some of the other things to work.
Anyway to automatically map between string/attribute?
from your description, looks like you're comparing a string to a symbol. Probably need to add:
define_method "#{status_name}=" do
self.status = status_name.to_sym
end
or do a #to_s on the status_names