I am building a Rails 3.2.11 application with Mongoid. I test with Cucumber and create test objects with FactoryGirl. I have embedded objects. I want to be able to use FactoryGirl traits with both my parent and embedded objects to make lots of permutations and keep things DRY.
The problem: I can't seem to get traits to apply to the embedded objects.
model.rb (actually, separate model files)
class User
include Mongoid::Document
#Fields and stuff
embeds_one :car
end
class Car
include Mongoid::Document
#Fields and stuff
embedded_in :user
end
spec/factories.rb
FactoryGirl.define do
factory :user do
status 'active' # shared attribute
trait :bob do
name 'Bob'
email 'bob#bob.com'
end
trait :carol do
name 'Carol'
email 'carol#carol.com'
end
car { [FactoryGirl.build(:car)] }
end
factory :car do
number_of_tires 4 # shared attribute
trait :red_ford do
make 'Ford'
color 'Red'
end
trait :blue_chevy do
make 'Chevy'
color 'Blue'
end
end
end
features/step_definitions/user_steps.rb (not working right)
Given /^There is a user Bob with a Blue Chevy$/ do
FactoryGirl.create(:user, :bob, :car => [:car => :blue_chevy])
end
I can factory create the user object just fine if I leave off the embedded object trait. Any way to get the embedded object to be built with the desired trait applied? Thanks!
The fix
Joshua Clayton at Thoughtbot http://joshuaclayton.me says:
There's no shorthand syntax for applying traits to associations;
basically, you'd have to do something like this:
cars = [FactoryGirl.build(:car, :blue_chevy)]
FactoryGirl.create(:user, :bob, cars: cars)
Because it is a 1 to 1 relationship, you do not need the array when building the car object in spec/factories.rb
car { FactoryGirl.build(:car) }
This applies in the cucumber step as well. Also, as I understand, traits are used as attributes, so your step code should look like
FactoryGirl.create(:user, :bob, :car, :blue_chevy)
Related
I am using RSpec and FactoryGirl to test my Ruby-on-Rails-3 application. I am using a hierarchy of factories:
FactoryGirl.define do
factory :local_airport do
... attributes generic to a local airport
factory :heathrow do
name "London Heathrow"
iata "LHR"
end
factory :stansted do
name "Stansted"
iata "STN"
end
... more local airports
end
end
In my RSpec I sometimes want to be able to create all the child factories by specifying the parent factory. Ideally, something like:
describe Flight do
before( :each ) do
# Create the standard airports
FactoryGirl.find( :local_airport ).create_child_factories
end
end
Many thanks in advance.
You can't really tell a factory to build all it's child factories, as being a child factory just means that it inherits the attributes from the parent. But what you could do is add a trait, for example :with_child_factories. Then your factory would look like the following:
FactoryGirl.define do
factory :local_airport do
... attributes generic to a local airport
factory :heathrow do
name "London Heathrow"
iata "LHR"
end
factory :stansted do
name "Stansted"
iata "STN"
end
... more local airports
trait :with_child_factories do
after(:create) do
FactoryGirl.create(:heathrow)
FactoryGirl.create(:stansted)
...
end
end
end
end
Then you could call it in your test with
FactoryGirl.create(:local_airport, :with_child_factories)
Well after a few hours of delving in the FactoryGirl code I have found a solution. Interestingly FactoryGirl only stores a reference to the parent in the factory, and not from parents to children.
In spec/factories/factory_helper.rb:
module FactoryGirl
def self.create_child_factories( parent_factory )
FactoryGirl.factories.each do |f|
parent = f.send( :parent )
if !parent.is_a?(FactoryGirl::NullFactory) && parent.name == parent_factory
child_factory = FactoryGirl.create( f.name )
end
end
end
end
In my RSpec I can now write:
require 'spec_helper'
describe Flight do
before( :each ) do
# Create the standard airports
FactoryGirl.create_child_factories( :local_airport )
end
...
One gotcha I found is that it is best to have a factory hierarchy that is simple (i.e. two levels). I started with a three layer hierarchy and found I was generating 'abstract' factories that only existed as part of the hierarchy. I have simplified the hierarchy to two levels using traits.
There aren't currently any up to date answers for this using Factory Girl 4.1 (that I could find) - how do you setup a many to many relationship inside of a factory?
For instance I have Students and Classrooms which are in a many to many relationship using a join table, so far I had the following setup:
factory :classroom do
name "Foo Class"
...
end
factory :student do
name "John Doe"
...
end
factory :student_with_classroom, :parent => :student do
after(:build) {|student| student.classrooms << classroom}
end
However this results in:
NameError:
undefined local variable or method `classroom' for #<FactoryGirl::SyntaxRunner>
My attempt was guesswork for the most part as I had no luck finding any non-deprecated syntax to accomplish this.
Actually I managed to find the answer I was looking for buried under a slew of other answers in this SO: How to create has_and_belongs_to_many associations in Factory girl
factory :classroom do
name "Foo Class"
...
end
factory :student do
name "John Doe"
...
end
factory :student_with_classroom, :parent => :student do
classrooms {[FactoryGirl.create(:classroom)]}
end
Check out this SO post: How to set up factory in FactoryGirl with has_many association. It will point you to https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md; search for has_many.
I have two models that are connected via a has_many/belongs_to association:
Class Project < ActiveRecord::Base
has_many :tasks
end
Class Tasks < ActiveRecord::Base
belongs_to :project
end
Each of the tasks are tagged with a HABTM relationship:
Class Tasks < ActiveRecord::Base
belongs_to :project
has_and_belongs_to_many :tags
end
I am trying to get a list of projects based on a tag id. I can get a list of projects that have tasks with a specific tag by using a class method on my Project model:
def by_tag(tag_id)
Project.joins(:tasks => :tags).where(:tags => {:id = tag_id})
end
Ideally, I'm looking to be able to list all the projects and their associated tasks for a given tag in my view. I could normally get a list of tasks belonging to a given project by using project.tasks if I used a typical find with project like Project.find(1).
However, when I try project.tasks on results found using my new class method Project.by_tag(1), I get a "NoMethodError: Undefined Method 'tasks'" error.
I looked into Named Scopes to get the Project by Tag results but it seems like people are moving away from that approach in favor of class methods. Is that true?
On your project model you need to add it to the class not the instance. Also note that this raises the self object to the class so you can eliminate "Project." unless you want to be explicit.
class << self
def by_tag(tag_id)
joins(:tasks => :tags).where(:tags => {:id = tag_id})
end
end
There is always debate over what is the best method. I myself prefer whatever gets the job done quicker. I like scopes personally but to each his own.
I came across a peculiar problem with has_one association in combination with an Object method override. Can somebody explain to me what is going on?
Here is how it goes:
I have a has_one relationship between Supplier and Account, like in the example of the has_one example used in Rails Guides.
Supplier:
class Supplier < ActiveRecord::Base
validates :name, :presence => true
has_one :account
nilify_blanks
end
Account:
class Account < ActiveRecord::Base
belongs_to :supplier
validates :supplier_id, :presence => true
nilify_blanks
def foo
puts 'in account'
end
def to_s
puts 'in account'
end
end
I also have a method foo on Object as follows:
class Object
def foo
puts 'in object'
end
end
When I call:
Supplier#account#to_s
I get 'in account'
When I call:
Supplier#account#foo
I get 'in object'
whereas I would expect it to print 'in account'
Does anybody have any clue why does this happen? Is this a bug in Rails ActiveRecord?
Thanks in advance
P.S. If you want, you can get a full fledged application that demonstrates the problem from here:
https://github.com/pmatsinopoulos/test_association_and_object_method_override.git
After doing some investingation with one of my friends, got the way assoiciation works.
when we do Supplier.account it will give you object of AssociationProxy not an object of account.
AssociationProxy delegates all methods to associated object if its definition not present in itself(it also delegates methods like class, inspect etc. so you can get the actual class name).
Now, when we add foo in Object class its available in AssociationProxy and when you say Supplier.account it invokes foo from AssociationProxy not from account.
if you want to invoke foo from account use target method to get actual account object like
Supplier.account.target.foo #=> foo from account
I've got some models that look like this:
class Basket
has_many :fruits, :dependent => :destroy
end
class Fruit
belongs_to :basket # do I need a polymorphic association here?
end
class Apple < Fruit
validate :crunchy
end
class Banana < Fruit
validate :peelable
end
Fruit is abstract in the sense that you never create, update, etc., Fruits, but rather Apples or Bananas. That means that I can't write something like edit_fruit_path(#fruit) in my views and have it automatically resolve.
What should I write in my views so that it always resolves to edit_apple_path(#fruit) or edit_banana_path(#fruit)?
This is not a polymorphic but rather a Single Table Inheritance.
I am assuming that you will inherit ActiveRecord::Base to Fruit.
Add the column type to your fruits table.
Now you can do edit_fruit_path(#apple) and it will be the Apple object.