Factory Girl Alternative

Hi All,

Shameless plug here, but just released my first gem which is a simple factory plugin a la Factory Girl, but which IMHO gives you much more control of your factory logic, check it out if interested:

Github: http://github.com/ginty/Cranky

Blog: http://www.hyperdecade.com/2010/05/26/cranky-at-factory-girl

Ginty wrote:

Hi All,

Shameless plug here, but just released my first gem which is a simple factory plugin a la Factory Girl, but which IMHO gives you much more control of your factory logic, check it out if interested:

Github: GitHub - ginty/cranky: A simple yet powerful test factory framework that makes it very easy to define your own factories.

Blog: http://www.hyperdecade.com/2010/05/26/cranky-at-factory-girl

Interesting. What distinguishes this from Machinist/Fixjour/Fixture Replacement?

Best,

There are many factories, but this one is mine.....admittedly that could be it to some extent!

To me the benefits are:

It is extremely light, which means it ain't gonna break on you when other components of your test environment change.

No need to pass any blocks about as arguments (I really hate that, but that could just be me).

The syntax could not be simpler and less obtrusive.

It doesn't provide any magic for dealing with associations, you're own your own. This was probably the #1 reason I developed this for myself, I just never really felt in control of this from the other solutions I tried (admittedly I haven't tried all of the ones on your list) and I feel much happier writing tests when I know now exactly when and how secondary instances are being generated.

Ginty wrote:

There are many factories, but this one is mine.....admittedly that could be it to some extent!

To me the benefits are:

It is extremely light, which means it ain't gonna break on you when other components of your test environment change.

That sounds good.

No need to pass any blocks about as arguments (I really hate that, but that could just be me).

Er...what's wrong with blocks? I don't get the problem, and anyway, they're part of idiomatic Ruby...

The syntax could not be simpler and less obtrusive.

Great!

It doesn't provide any magic for dealing with associations, you're own your own. This was probably the #1 reason I developed this for myself, I just never really felt in control of this from the other solutions I tried (admittedly I haven't tried all of the ones on your list) and I feel much happier writing tests when I know now exactly when and how secondary instances are being generated.

What's the point of factories that don't handle associations? That seems like a huge disadvantage. Part of the reason that I use factories is so I can just say Widget.make and get a fully viable Widget object, complete with required fields and associations. Otherwise, why bother?

Best,

Ginty wrote: > There are many factories, but this one is mine.....admittedly that > could be it to some extent!

> To me the benefits are:

> It is extremely light, which means it ain't gonna break on you when > other components of your test environment change.

That sounds good.

> No need to pass any blocks about as arguments (I really hate that, but > that could just be me).

Er...what's wrong with blocks? I don't get the problem, and anyway, they're part of idiomatic Ruby...

Blocks as wrappers, iterators, etc....very nice, blocks as arguments to your own methods....meh, blocks as arguments to black box methods...not really for me.

> The syntax could not be simpler and less obtrusive.

Great!

> It doesn't provide any magic for dealing with associations, you're own > your own. This was probably the #1 reason I developed this for myself, > I just never really felt in control of this from the other solutions I > tried (admittedly I haven't tried all of the ones on your list) and I > feel much happier writing tests when I know now exactly when and how > secondary instances are being generated.

What's the point of factories that don't handle associations? That seems like a huge disadvantage. Part of the reason that I use factories is so I can just say Widget.make and get a fully viable Widget object, complete with required fields and associations. Otherwise, why bother?

Agreed not much point to that. I didn't say that it doesn't handle them, I said that it doesn't provide any behind the scenes magic to handle them. In your factory definition for the widgets association you would either just call your factory method for the other item, or if you wanted something more elaborate (all widgets with the exact same parent for example) your own helper method.

Here's the example from the README for a user and an address factory definition, and how you relate them:

def user   # Define attributes via a hash, generate the values any way you want   define :name => "Jimmy",          # An 'n' counter method is available for uniqueness          :email => "jimmy#{ n } @home.com",          :role => :user,          # Call your own association helper methods          :address => default_address          # This would have worked also if you just want any address...          # :address => create(:address) end

# Return a default address if it already exists, or call the address # factory to make one def default_address   @default_address ||= create(:address) end

# Alternatively loose the DSL altogether and handle all yourself # still callable via Factory.build(:address) def address   a = Address.new   a.street = "192 Broadway"   # You can get any caller overrides via the options hash   a.city = options[:city] || "New York"   a # Only rule is the method must return the generated object end

Anyway if you want any more info check out GitHub - ginty/cranky: A simple yet powerful test factory framework that makes it very easy to define your own factories. it's well documented.

Ginty wrote:

> No need to pass any blocks about as arguments (I really hate that, but > that could just be me).

Er...what's wrong with blocks? �I don't get the problem, and anyway, they're part of idiomatic Ruby...

Blocks as wrappers, iterators, etc....very nice, blocks as arguments....meh.

Uh, no. Passing closures (blocks) as arguments is one feature that makes Ruby very powerful. You'll have to do better than "meh" to explain why you're choosing not to use a key Ruby feature, at least if you want people to take you seriously and use your software.

> I just never really felt in control of this from the other solutions I > tried (admittedly I haven't tried all of the ones on your list) and I > feel much happier writing tests when I know now exactly when and how > secondary instances are being generated.

What's the point of factories that don't handle associations? �That seems like a huge disadvantage. �Part of the reason that I use factories is so I can just say Widget.make and get a fully viable Widget object, complete with required fields and associations. �Otherwise, why bother?

Agreed not much point to that. I didn't say that it doesn't provide any behind the scenes magic to handle them. In your factory definition for the widgets association you would either just call the create method for the other item, or if you want something more elaborate (all widgets with the exact same parent for example) your own helper method.

Here's the example from the README for a user and an address factory, and how you relate them.

def user   # Define attributes via a hash, generate the values any way you want   define :name => "Jimmy",          # An 'n' counter method is available to help make things unique

Does it work with Faker?

         :email => "jimmy#{n}@home.com",          :role => :user,          # Call your own helper methods to wire up your associations...

Does :user just get passed to a helper, or what?

And what didn't you feel in control of with Machinist or Factory Girl? Machinist in particular just uses the usual Rails association mechanisms.

         :address => default_address          # This would have worked also if you just want any address...

end

# Return a default address if it already exists, or call the address # factory to make one def default_address   @default_address ||= create(:address) end

# Alternatively loose the DSL altogether and define the factory yourself, # still callable via Factory.build(:address) def address   a = Address.new   a.street = "192 Broadway"   # You can get any caller overrides via the options hash   a.city = options[:city] || "New York"   a # Only rule is the method must return the generated object end

end

Anyway if you want any more info check out GitHub - ginty/cranky: A simple yet powerful test factory framework that makes it very easy to define your own factories. it's well documented.

I'll check it out. Based on what you've so far said, I doubt that I'll replace Machinist with Cranky anytime soon, but I'm always open to pleasant surprises.

Best,

Ginty wrote: Ginty wrote: [...]

def user   # Define attributes via a hash, generate the values any way you want   define :name => "Jimmy",          # An 'n' counter method is available to help make things unique          :email => "jimmy#{n}@home.com",

I just realized something else. Because you're not passing a block here, your counter method is useless -- the variable interpolations in that string will only be done once, and every call of the factory thereafter will set email to "jimmy1@home.com". There is *no possible way* for this syntax to work as you intended. The *only* way to get this to work is with a block (or with eval, but that's silly).

In other words, your aversion to blocks as arguments appears to be breaking your software.

Best,

Marnen Laibow-Koser wrote::

Ginty wrote: [...]

def user   # Define attributes via a hash, generate the values any way you want   define :name => "Jimmy",          # An 'n' counter method is available to help make things unique          :email => "jimmy#{n}@home.com",

I just realized something else. Because you're not passing a block here, your counter method is useless -- the variable interpolations in that string will only be done once, and every call of the factory thereafter will set email to "jimmy1@home.com". There is *no possible way* for this syntax to work as you intended. The *only* way to get this to work is with a block (or with eval, but that's silly).

On third thought, this may not be true either. However, I really don't like the need to define a separate user method just to avoid passing blocks.

In other words, your aversion to blocks as arguments appears to be breaking your software.

On reflection, perhaps not; but it does appear to be causing lots of silly contortions in your code.

If you don't like the core language features, then I suggest (for your own sanity) that you find a language more compatible with your way of thinking. Don't try to make Ruby into (say) PHP, or PHP into Ruby.

Ginty wrote:

>> > No need to pass any blocks about as arguments (I really hate that, but >> > that could just be me).

>> Er...what's wrong with blocks? I don't get the problem, and anyway, >> they're part of idiomatic Ruby...

> Blocks as wrappers, iterators, etc....very nice, blocks as > arguments....meh.

Uh, no. Passing closures (blocks) as arguments is one feature that makes Ruby very powerful. You'll have to do better than "meh" to explain why you're choosing not to use a key Ruby feature, at least if you want people to take you seriously and use your software.

Haha, yeah you've probably taken me down a path here I didn't really mean to go down.

Marnen says: I didn't lead you down ...

Yeah I know.

Bottom line, this to me is ugly:

u.email {|a| "#{a.first_name}.#{a.last_name}@example.com" }

this is more elegant in it's simplicity:

:email => "#{first_name}.#{last_name}@example.com"

Personal preference.

>> > I just never really felt in control of this from the other solutions I >> > tried (admittedly I haven't tried all of the ones on your list) and I >> > feel much happier writing tests when I know now exactly when and how >> > secondary instances are being generated.

>> What's the point of factories that don't handle associations? That >> seems like a huge disadvantage. Part of the reason that I use factories >> is so I can just say Widget.make and get a fully viable Widget object, >> complete with required fields and associations. Otherwise, why bother?

> Agreed not much point to that. > I didn't say that it doesn't provide any behind the scenes magic to > handle them. In your factory definition for the widgets association > you would either just call the create method for the other item, or if > you want something more elaborate (all widgets with the exact same > parent for example) your own helper method.

> Here's the example from the README for a user and an address factory, > and how you relate them.

> def user > # Define attributes via a hash, generate the values any way you want > define :name => "Jimmy", > # An 'n' counter method is available to help make things > unique

Does it work with Faker?

Yeah it'll work with anything like that, you're just defining a regular method, require what you will.

> :email => "jimmy#...@home.com", > :role => :user, > # Call your own helper methods to wire up your > associations...

Does :user just get passed to a helper, or what?

Good point, maybe a bad example, :user was just the value I pass to my user.role= method. It doesn't refer to the user factory, model or otherwise. Could equally have been :admin or what have you. I'll change this in the docs.

And what didn't you feel in control of with Machinist or Factory Girl? Machinist in particular just uses the usual Rails association mechanisms.

For me they are too heavy, too much magic at times (admittedly I am a Factory Girl guy), I need to think too much about how to create an association if I want anything other than a randomly generated one, and ultimately I just don't like the syntax. The combination of these things has made me feel that I've never really 'flowed' with this aspect of writing my tests for want of a better description.

I said in the blog that Miniskirt was a real revelation to me as it showed me that these factory helpers don't need to do much, I don't quite understand now why some of them are so big under the hood, but Miniskirt didn't hit the syntax aspect for me either.

So Cranky is now my factory of choice, it feels good, very good, and I don't think that I have abnormal tastes. So I've packaged it up should others like it to. So if you do cool, if you don't that's cool to, we're not exactly short of choice here.

Marnen Laibow-Koser wrote:: > Ginty wrote: > [...] >> def user >> # Define attributes via a hash, generate the values any way you want >> define :name => "Jimmy", >> # An 'n' counter method is available to help make things >> unique >> :email => "jimmy#...@home.com",

> I just realized something else. Because you're not passing a block > here, your counter method is useless -- the variable interpolations in > that string will only be done once, and every call of the factory > thereafter will set email to "jim...@home.com". There is *no possible > way* for this syntax to work as you intended. The *only* way to get > this to work is with a block (or with eval, but that's silly).

On third thought, this may not be true either. However, I really don't like the need to define a separate user method just to avoid passing blocks.

Yes you are indeed talking piffle here. If you prefer a block to a straight method declaration then this is not for you, fair enough.

> In other words, your aversion to blocks as arguments appears to be > breaking your software.

On reflection, perhaps not; but it does appear to be causing lots of silly contortions in your code.

If you don't like the core language features, then I suggest (for your own sanity) that you find a language more compatible with your way of thinking. Don't try to make Ruby into (say) PHP, or PHP into Ruby.

Nope sorry.

Ginty wrote:

> arguments....meh.

Uh, no. �Passing closures (blocks) as arguments is one feature that makes Ruby very powerful. �You'll have to do better than "meh" to explain why you're choosing not to use a key Ruby feature, at least if you want people to take you seriously and use your software.

Haha, yeah you've probably taken me down a path here I didn't really mean to go down.

Marnen says: I didn't lead you down ...

Yeah I know.

Bottom line, this to me is ugly:

u.email {|a| "#{a.first_name}.#{a.last_name}@example.com" }

this is more elegant in it's simplicity:

:email => "#{first_name}.#{last_name}@example.com"

Personal preference.

For a trivial example like that, sure. But it doesn't generalize to anything more complex. Blocks do.

>> is so I can just say Widget.make and get a fully viable Widget object, > and how you relate them.

> def user > � # Define attributes via a hash, generate the values any way you want > � define :name � �=> "Jimmy", > � � � � �# An 'n' counter method is available to help make things > unique

Does it work with Faker?

Yeah it'll work with anything like that, you're just defining a regular method,

I noticed that. On what class?

require what you will.

> � � � � �:email � => "jimmy#...@home.com", > � � � � �:role � �=> :user, > � � � � �# Call your own helper methods to wire up your > associations...

Does :user just get passed to a helper, or what?

Good point, maybe a bad example, :user was just the value I pass to my user.role= method.

Why on earth would you assign a symbol to what I assume is an association?

It doesn't refer to the user factory, model or otherwise. Could equally have been :admin or what have you. I'll change this in the docs.

And what didn't you feel in control of with Machinist or Factory Girl? Machinist in particular just uses the usual Rails association mechanisms.

For me they are too heavy, too much magic at times (admittedly I am a Factory Girl guy), I need to think too much about how to create an association if I want anything other than a randomly generated one, and ultimately I just don't like the syntax.

I'm not all that fond of FG's association syntax either. But for Machinist, it really is just Post.make.comments.make or Comment.make :post => Post.make just like Rails. If you find that hard to understand, I don't know what more I can say.

The combination of these things has made me feel that I've never really 'flowed' with this aspect of writing my tests for want of a better description.

Then try Machinist!

I said in the blog that Miniskirt was a real revelation to me as it showed me that these factory helpers don't need to do much, I don't quite understand now why some of them are so big under the hood, but Miniskirt didn't hit the syntax aspect for me either.

I've never even heard of it.

So Cranky is now my factory of choice, it feels good, very good, and I don't think that I have abnormal tastes. So I've packaged it up should others like it to. So if you do cool, if you don't that's cool to, we're not exactly short of choice here.

I think that in your quest for "simplicity", you've wound up with something that seems to take twice as much work as Machinist to set up a usable test environment.

Best,

Ginty wrote: >> > arguments....meh.

>> Uh, no. Passing closures (blocks) as arguments is one feature that >> makes Ruby very powerful. You'll have to do better than "meh" to >> explain why you're choosing not to use a key Ruby feature, at least if >> you want people to take you seriously and use your software.

> Haha, yeah you've probably taken me down a path here I didn't really > mean to go down.

> Marnen says: I didn't lead you down ...

> Yeah I know.

> Bottom line, this to me is ugly:

> u.email {|a| "#{a.first_name}.#{a.last_na...@example.com" }

> this is more elegant in it's simplicity:

> :email => "#{first_name}.#{last_na...@example.com"

> Personal preference.

For a trivial example like that, sure. But it doesn't generalize to anything more complex. Blocks do.

>> >> is so I can just say Widget.make and get a fully viable Widget object, >> > and how you relate them.

>> > def user >> > # Define attributes via a hash, generate the values any way you want >> > define :name => "Jimmy", >> > # An 'n' counter method is available to help make things >> > unique

>> Does it work with Faker?

> Yeah it'll work with anything like that, you're just defining a > regular method,

I noticed that. On what class?

> require what you will.

>> > :email => "jimmy#...@home.com", >> > :role => :user, >> > # Call your own helper methods to wire up your >> > associations...

>> Does :user just get passed to a helper, or what?

> Good point, maybe a bad example, :user was just the value I pass to my > user.role= method.

Why on earth would you assign a symbol to what I assume is an association?

It's an attribute.

> It doesn't refer to the user factory, model or > otherwise. Could equally have been :admin or what have you. I'll > change this in the docs.

>> And what didn't you feel in control of with Machinist or Factory Girl? >> Machinist in particular just uses the usual Rails association >> mechanisms.

> For me they are too heavy, too much magic at times (admittedly I am a > Factory Girl guy), I need to think too much about how to create an > association if I want anything other than a randomly generated one, > and ultimately I just don't like the syntax.

I'm not all that fond of FG's association syntax either. But for Machinist, it really is just Post.make.comments.make or Comment.make :post => Post.make just like Rails. If you find that hard to understand, I don't know what more I can say.

> The combination of these > things has made me feel that I've never really 'flowed' with this > aspect of writing my tests for want of a better description.

Then try Machinist!

> I said in the blog that Miniskirt was a real revelation to me as it > showed me that these factory helpers don't need to do much, I don't > quite understand now why some of them are so big under the hood, but > Miniskirt didn't hit the syntax aspect for me either.

I've never even heard of it.

> So Cranky is now my factory of choice, it feels good, very good, and I > don't think that I have abnormal tastes. So I've packaged it up should > others like it to. So if you do cool, if you don't that's cool to, > we're not exactly short of choice here.

I think that in your quest for "simplicity", you've wound up with something that seems to take twice as much work as Machinist to set up a usable test environment.

Yep I think we're done here, I don't deny that Machinist may be good, but I think this is good to.

If this has aroused anyone's interest please have a look at the README and judge for yourself.

It couldn't be simpler to set up test environment.

Mr Marnen!

Been thinking about this today and I see now that my aversion to block arguments was unfounded.

I think I probably went off on a tangent since I have never really needed this when defining my factories, and I was always irked that I was being forced to use a block by my factory of choice (FG) for something as simple as some string substitutions.

Rather whenever I have needed more complex code to generate the attribute it has been something that I have needed across multiple factories where a shared function has been more appropriate, and this I have often found less than intuitive to achieve with FG.

So this background has been reflected in my choice of syntax here, which makes it very easy/clear how to share helper methods across factories and dispenses with the need for a block when a simple string substitution will do.

That being said I can see now that for the one-off cases being able to use an anonymous function could come in very handy indeed.

Anyway I've added the ability to pass in attributes via a block argument to my plugin should anyone find it useful. Who knows I may even use it myself one day.

Cheers.