factory_girl question

I am still trying to wrap my head around FG… can someone explain me why I am seemingly having to reload an object to have access to the correct association value which I updated since creating the object with FG. Here is an example (Factory below also):

@person = Factory(:person) @person.addresses.size.should == 0 address = Factory(:address) @person.add_address(address) @person = Person.find(@person.id) # if I dont do this I get the old value, 0, but if I reload I get the right value, 1. @person.addresses.size.should == 1 @person.addresses[0].person_id.should == @person.id

Factory.define :person do |f| f.first_name {“MyString”} f.middle_name {“MyString”} f.last_name {“MyString”} end

Factory.define :address do |f| f.person_id {1} f.address {“MyString”} f.zip {“94587”} f.city {“MyString”} f.state {“MyString”} end

David Kahn wrote in post #968158:

I am still trying to wrap my head around FG...

What's to wrap your head around? All it does is create an object with some associations. If you're finding it confusing, you're most likely overthinking it.

can someone explain me why I am seemingly having to reload an object to have access to the correct association value which I updated since creating the object with FG. Here is an example (Factory below also):

@person = Factory(:person) @person.addresses.size.should == 0 address = Factory(:address) @person.add_address(address)

Does add_address save @person ?

Personally, I would do @person.addresses << address , which always seems to do the right thing.

@person = Person.find(@person.id) # if I dont do this I get the old value, 0, but if I reload I get the right value, 1. @person.addresses.size.should == 1 @person.addresses[0].person_id.should == @person.id

You do know that, while this test is useful for getting to know Factory Girl, it is of no use for an actual application, right? All it's doing is testing FG and AR functionality that's already well tested.

Factory.define :person do |f|   f.first_name {"MyString"}   f.middle_name {"MyString"}   f.last_name {"MyString"} end

Factory.define :address do |f|   f.person_id {1}   f.address {"MyString"}   f.zip {"94587"}   f.city {"MyString"}   f.state {"MyString"} end

I don't think you need to use blocks for constant values.

However, once you get FG working, I highly recommend using Faker to generate the data.

Best,

David Kahn wrote in post #968158:

I am still trying to wrap my head around FG…

What’s to wrap your head around? All it does is create an object with

some associations. If you’re finding it confusing, you’re most likely

overthinking it.

can someone explain me

why I

am seemingly having to reload an object to have access to the correct

association value which I updated since creating the object with FG.

Here is

an example (Factory below also):

@person = Factory(:person)

@person.addresses.size.should == 0

address = Factory(:address)

@person.add_address(address)

Does add_address save @person ?

No, the call does just save the new address. I pass it this way as I want to ensure the person object id gets attached to the new address.

Personally, I would do @person.addresses << address , which always seems

to do the right thing.

@person = Person.find(@person.id) # if I dont do this I get the old

value,

0, but if I reload I get the right value, 1.

@person.addresses.size.should == 1

@person.addresses[0].person_id.should == @person.id

You do know that, while this test is useful for getting to know Factory

Girl, it is of no use for an actual application, right? All it’s doing

is testing FG and AR functionality that’s already well tested.

Right… but when I start having a problem I write more test code, that is how I got here…

Factory.define :person do |f|

f.first_name {“MyString”}

f.middle_name {“MyString”}

f.last_name {“MyString”}

end

Factory.define :address do |f|

f.person_id {1}

f.address {“MyString”}

f.zip {“94587”}

f.city {“MyString”}

f.state {“MyString”}

end

I don’t think you need to use blocks for constant values.

Right, that was an oversight

However, once you get FG working, I highly recommend using Faker to

generate the data.

I 'll try it when I have a chance… thanks

David Kahn wrote in post #968171:

> association value which I updated since creating the object with FG. > Here is > an example (Factory below also): > > @person = Factory(:person) > @person.addresses.size.should == 0 > address = Factory(:address) > @person.add_address(address)

Does add_address save @person ?

No, the call does just save the new address.

So it's not changing @person at all, which means that Rails has no way of knowing that the association has changed. That's probably why you're getting the results you are.

I pass it this way as I want to ensure the person object id gets attached to the new address.

@person.addresses << address does likewise, but I believe it *does* save or reload @person.

Best,

David Kahn wrote in post #968171:

association value which I updated since creating the object with FG.

Here is

an example (Factory below also):

@person = Factory(:person)

@person.addresses.size.should == 0

address = Factory(:address)

@person.add_address(address)

Does add_address save @person ?

No, the call does just save the new address.

So it’s not changing @person at all, which means that Rails has no way

of knowing that the association has changed. That’s probably why you’re

getting the results you are.

Thank you, that makes a lot of sense. The fog is clearing.

I pass it this way as I

want to

ensure the person object id gets attached to the new address.

@person.addresses << address does likewise, but I believe it does save

or reload @person.

Right, at least in the past this has worked in Rails 2.

Marnen, you consistently take Rails' hallmark "opinionated software" up a couple of orders of magnitude :wink: so I'm curious about your opinion on FG vs Machinist. This isn't flame bait; I really am curious as I'm deliberating between the two. TIA.

Stan Kaufman wrote in post #968201:

However, once you get FG working, I highly recommend using Faker to generate the data.

Marnen, you consistently take Rails' hallmark "opinionated software" up a couple of orders of magnitude :wink:

I'm almost afraid to ask how you meant that... :slight_smile:

so I'm curious about your opinion on FG vs Machinist. This isn't flame bait; I really am curious as I'm deliberating between the two. TIA.

I've been using Machinist for personal projects and Factory Girl at my current job. I think I find Machinist friendlier, but the two libraries are pretty close at this point.

Best,

Side note about FG usage: I was under the impression that this is suboptimal for FG because now Factory(:address) generates and invalid object unless you do something special. I traditionally write this as like this:

Factory.define :address do |f|   f.association :person   f.address {"MyString"}   f.zip {"94587"}   f.city {"MyString"}   f.state {"MyString"} end

This way if I want an address to test and I don't care about it's person, then Factory(:address) gives me a valid item. And then if I do care about the association then we do:

@person = Factory(:person) @person.addresses.size.should == 0 @address = Factory(:address, :person => @person) @person.address.size.should == 0 @person.reload.address.size.should == 1

Keep in mind that in this case you don't want to do:

@person = Factory(:person) @address = Factory(:address) @person.addresses << @address

Because the second line will generate another, unneeded Person object.

\Peter

@person = Factory(:person)

@person.addresses.size.should == 0

address = Factory(:address)

@person.add_address(address)

@person = Person.find(@person.id) # if I dont do this I get the old value,

0, but if I reload I get the right value, 1.

@person.addresses.size.should == 1

@person.addresses[0].person_id.should == @person.id

Factory.define :person do |f|

f.first_name {“MyString”}

f.middle_name {“MyString”}

f.last_name {“MyString”}

end

Factory.define :address do |f|

f.person_id {1}

f.address {“MyString”}

f.zip {“94587”}

f.city {“MyString”}

f.state {“MyString”}

end

Side note about FG usage: I was under the impression that this is

suboptimal for FG because now Factory(:address) generates and invalid

object unless you do something special. I traditionally write this as

like this:

Factory.define :address do |f|

f.association :person f.address {“MyString”}

f.zip {“94587”}

f.city {“MyString”}

f.state {“MyString”}

end

This way if I want an address to test and I don’t care about it’s

person, then Factory(:address) gives me a valid item. And then if I

do care about the association then we do:

@person = Factory(:person)

@person.addresses.size.should == 0

@address = Factory(:address, :person => @person)

@person.address.size.should == 0

@person.reload.address.size.should == 1

Keep in mind that in this case you don’t want to do:

@person = Factory(:person)

@address = Factory(:address)

@person.addresses << @address

Because the second line will generate another, unneeded Person object.

Thanks… this helps

ppgengler@prevailhs.com wrote in post #968310: [...]

Keep in mind that in this case you don't want to do:

@person = Factory(:person) @address = Factory(:address) @person.addresses << @address

Because the second line will generate another, unneeded Person object.

Hold it. If there's a habtm relationship between Person and Address, then Addresses shouldn't even have a person_id. OTOH, if there isn't a habtm relationship, then the << operation is unnecessary (and I think it's a syntax error).

So...which is it?

\Peter

Best,

Factory.define :person do |f| f.first_name {“MyString”}

f.middle_name {“MyString”} f.last_name {“MyString”}

end

Factory.define :address do |f| f.person_id {1} f.address {“MyString”} f.zip {“94587”} f.city {“MyString”} f.state {“MyString”} end

you pass a block like this if you need reevaluation like when using a rand function or faker, it makes no sense to do it with a fixed value

f.address ’ foo’ is the same as f.address { ‘foo’ }

the first and second address created will have the same value in both cases

f.zip rand(1000) the first and second record created will have the same value

f.zip { rand(1000) } each record will have a unique value

so you should add faker and then

f.city {Faker::Address.city}

will give you a differente city for each record

In his original example Address has a person_id field so I was assuming a has_many relationship, not a HABTM. However, though I'd want to reflect on it further, I'm pretty sure it doesn't change anything here as long as you deal with the relationship name everywhere and not the ID fields; Rails will just do The Right Thing (tm) and add/remove records in the join table appropriately.

The << is necessary in the second example to be comparable to his original example where he wanted to create an @person object, then create an @address object and have them linked; the @address at first will point to a different Person object than @person, so using the << on the collection will add it (i.e. it will set person_id on @address to @person.id). It does work BTW, I just tested it again in console to be sure (and its listed at the top of the added methods here: has_many (ActiveRecord::Associations::ClassMethods) - APIdock, and if we care about the HABTM here: has_and_belongs_to_many (ActiveRecord::Associations::ClassMethods) - APIdock).

I think maybe what you're pointing out though, is that he could just do:

  @address = Factory(:address)

and then use @address.person?

If that's adequate then that's definitely cleaner (and quicker and more robust). However, what I read from the OP is that he has a need to create a Person object first, configured a specific way (although he didn't have any particular factory overrides, so maybe this is an incorrect assumption?). ~After~ that object is created he needs an Address that is linked to it. As such he either needs to 1) create the Address object with a stub Person, then associate with the one he wants afterwards (suboptimal, the usage of <<) or 2) pass in the Person object to the Address factory to link at creation time (preferred, AFAIK).

\Peter

ppgengler@prevailhs.com wrote in post #968602:

Hold it. If there's a habtm relationship between Person and Address, then Addresses shouldn't even have a person_id. OTOH, if there isn't a habtm relationship, then the << operation is unnecessary (and I think it's a syntax error).

So...which is it?

In his original example Address has a person_id field so I was assuming a has_many relationship, not a HABTM.

Which means << is invalid, doesn't it?

However, though I'd want to reflect on it further, I'm pretty sure it doesn't change anything here as long as you deal with the relationship name everywhere and not the ID fields; Rails will just do The Right Thing (tm) and add/remove records in the join table appropriately.

It's has_many, so there's no join table.

[...]

I think maybe what you're pointing out though, is that he could just do:

  @address = Factory(:address)

and then use @address.person?

No. That's not at all what I'm pointing out.

If that's adequate then that's definitely cleaner (and quicker and more robust). However, what I read from the OP is that he has a need to create a Person object first, configured a specific way (although he didn't have any particular factory overrides, so maybe this is an incorrect assumption?). ~After~ that object is created he needs an Address that is linked to it. As such he either needs to 1) create the Address object with a stub Person, then associate with the one he wants afterwards (suboptimal, the usage of <<) or 2) pass in the Person object to the Address factory to link at creation time (preferred, AFAIK).

That's correct.

Best,

Perhaps I'm not understanding what you mean when you say invalid? Do you mean that it will generate an exception, or that it isn't a good way to solve the problem?

\Peter

ppgengler@prevailhs.com wrote in post #968626:

Which means << is invalid, doesn't it?

Perhaps I'm not understanding what you mean when you say invalid? Do you mean that it will generate an exception, or that it isn't a good way to solve the problem?

I mean that << isn't defined for has_many, but on reflection I realize I was wrong. Just forget I said that. :smiley:

\Peter

Best,