Best way to modify factory attributes for multiple tests? - ruby-on-rails-3

Trying to test some routing with rspec and factories. What would be the best way to modify an existing factory multiple times inside the spec test?
require "spec_helper"
describe gameController do
describe "routing" do
game = FactoryGirl.create(:game)
it "routes to #show" do
get("/game/1").should route_to("game#show", :id => "1")
end
it "routes to #show" do
# need to modify 1 param of the factory.. how best to do this?
get("/game/1").should route_to("game#show", :id => "1")
end
end
end

You basically have two options. If you're just modifying a single param between test, it might be easiest just to do something like:
before(:each) do
game = FactoryGirl.create(:game)
end
it "does something" do
get("/game/1").should route_to("game#show", :id => "1")
end
it "does something else" do
game.update_attributes(:param => "value")
get("/game/1").should route_to("game#show", :id => "1")
end
Otherwise, you could set up a factory girl sequence and do a fresh FactoryGirl.create in each spec.

Related

How to skip after_build callback in factories?

I'm facing a problem while creating a factory. I have a factory like:
Factory.define :job do |j|
j.association :service_partner, :factory => :service_partner
j.price_per_task 1.to_money
end
j.after_build{|j| j.project.service_partner_ids = [j.service_partner.id] unless j.service_partner.nil?}
end
How can I skip the after_build while creating factory?
If you only want to have the after_build callback to run occasionally, your best bet is to define a nested factory:
Factory.define :job do |j|
j.association :service_partner, :factory => :service_partner
j.price_per_task 1.to_money
end
factory :job_with_additional_setup do
j.after_build{|j| j.project.service_partner_ids = [j.service_partner.id] unless j.service_partner.nil?}
end
end
You can then create a normal job by doing FactoryGirl.create(:job) or one with the after_build: FactoryGirl.create(:job_with_additional_setup)

Use the same model in two active admin classes

I'm working on an ActiveAdmin app for a large production application. I'm currently trying to use the same model for two activeadmin "entities".
So, say I have
class Person < ActiveRecord::Base
scope :special, where(:is_special => true)
scope :ordinary, where(:is_special => false)
end
Can I do something like
ActiveAdmin.register Person, :name => "Special People" do
# columns, filters for special people
controller do
def scoped_collection
Person.special
end
end
end
ActiveAdmin.register Person, :name => "Ordinary People" do
# columns, filters for ordinary people
controller do
def scoped_collection
Person.ordinary
end
end
end
(I'm making up the syntax a bit here to explain what I want to do.)
The two types of people would appear as menu items and different CRUD interfaces as defined in the ActiveAdmin.register block. They would just have the same underlying model.
Active Admin model Code:
ActiveAdmin.register Person, as: "Special People" do
scope :Special, default: true do |person|
person = Person.special
end
controller do
def scoped_collection
Person.special
end
end
end
ActiveAdmin.register Person, as: "Ordinary People" do
scope :Ordinary, default: true do |person|
person = Person.ordinary
end
controller do
def scoped_collection
Person.ordinary
end
end
end
Now in routes:
match '/admin/special_people/scoped_collection/:id' => 'admin/special_people#scoped_collection'
match '/admin/ordinary_people/scoped_collection/:id' => 'admin/ordinary_people#scoped_collection'
Try with above changes. Hope this would solve your issues. Thanks.

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

Singleton factories in factory_girl/machinist?

Is there some configuration in a factory of factory girl/machinist that forces it to create objects with the same factory name just once during test case and return the same instance all the time? I know, i can do something like:
def singleton name
##singletons ||= {}
##singletons[name] ||= Factory name
end
...
Factory.define :my_model do |m|
m.singleton_model { singleton :singleton_model }
end
but maybe there is a better way.
You can use the initialize_with macro inside your factory and check to see if the object already exists, then don't create it over again. This also works when said factory is referenced by associations:
FactoryGirl.define do
factory :league, :aliases => [:euro_cup] do
id 1
name "European Championship"
owner "UEFA"
initialize_with { League.find_or_create_by_id(id)}
end
end
There is a similar question here with more alternatives: Using factory_girl in Rails with associations that have unique constraints. Getting duplicate errors
#CubaLibre answer with version 5 of FactoryBot:
FactoryGirl.define do
factory :league do
initialize_with { League.find_or_initialize_by(id: id) }
sequence(:id)
name "European Championship"
end
end
Not sure if this could be useful to you.
With this setup you can create n products using the factory 'singleton_product'. All those products will have same platform (i.e. platform 'FooBar').
factory :platform do
name 'Test Platform'
end
factory :product do
name 'Test Product'
platform
trait :singleton do
platform{
search = Platform.find_by_name('FooBar')
if search.blank?
FactoryGirl.create(:platform, :name => 'FooBar')
else
search
end
}
end
factory :singleton_product, :traits => [:singleton]
end
You can still use the standard product factory 'product' to create a product with platform 'Test Platform', but it will fail when you call it to create the 2nd product (if platform name is set to be unique).