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: http://github.com/ginty/Cranky

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 http://github.com/ginty/Cranky
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
http://github.com/ginty/Cranky
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.