Created
October 1, 2010 12:25
-
-
Save josevalim/606129 to your computer and use it in GitHub Desktop.
Revisions
-
josevalim revised this gist
Oct 1, 2010 . 1 changed file with 26 additions and 27 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,50 +1,49 @@ # Rails developers have long had bad experiences with fixtures for # several reasons, including misuse. # # Misuse of fixtures is characterized by having a huge number of them, # requiring the developer to maintain a lot of data and creating dependencies # between tests. In my experience working (and rescuing) many applications, 80% # of fixtures are only used by 20% of tests. # # An example of such tests is one assuring that a given SQL query with # GROUP BY and ORDER BY conditions returns the correct result set. As expected, # a huge amount of data is needed for this test, most of which we won't be used # in other tests. # # For these scenarios factories are a fine solution. They won't clutter up # your database since they are created (and destroyed) during the execution # of specific tests and are easier to maintain as the underlying models change. # # I believe this was the primary reason for the Rails community to strongly # adopt factories builders over the few years. # # However, factories are also misused. Developers commonly create a huge # amount of data with factories before each test in an integration # suite, which causes their test suite to run slowly, where fixtures would # work great for this purpose. # # This is a small attempt to have the best of both worlds. # # For the data used in almost all your tests, simply use fixtures. For all the # other smaller scenarios, use factories. As both fixtures and factories # require valid attributes, this quick solution allows you to create small, # simple factories from the information stored in your fixtures. # # == Examples # # Define your builder inside the Builders module: # # module Builders # build :message do # { :title => "OMG", :queue => queues(:general) } # end # end # # The builder must return a hash. After defining this builder, # create a new message by calling +create_message+ or +new_message+ # in your tests. Both methods accepts an optional options # parameter that gets merged into the given hash. # # == Reusing fixtures # @@ -66,8 +65,8 @@ # == Just Ruby # # Since all Builders are defined inside the Builders module, without # a DSL on top of it, we can use Ruby to meet more complex needs, # like supporting sequences. # # module Builders # @@sequence = 0 -
josevalim revised this gist
Oct 1, 2010 . 1 changed file with 6 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -140,4 +140,9 @@ module Builders :profile => new_profile } end end test "users sets profile gravatar on save" do user = create_user! assert_equal Digest::MD5.hexdigest("[email protected]"), user.profile.gravatar end -
josevalim revised this gist
Oct 1, 2010 . 1 changed file with 31 additions and 18 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,22 +1,35 @@ # Rails developers have had bad experience with fixtures since a long time # due to several reasons, including misuse. # # This misuse of fixtures is often characterized by a huge ammount of # fixtures, causing a lot of data to maintain and dependence between tests. # In my experience working (and rescueing) different applications, 80% of # these fixtures are used only by 20% of tests. # # An example of such tests is a test that assures a given SQL query with # GROUP BY and ORDER BY conditions returns the correct result set. As expected, # we need a huge amount of data in this test which we usually don't need in # order tests. # # For such scenarios, factories are a fine solution. They won't cluster # all your database since they are created for these specific tests and they # are also easier to maintain. # # I believe this was the primary reason for the Rails community to strongly # adopt factories builders as we saw in the couple two years ago. # # However, factories are also misused. It is common to see people creating # a huge amount of data with factories before each test in their integration # suite, causing their whole test suite to be slow, while fixtures would # work great for this purpose. # # This is a small attempt to have the best of both worlds. # # For the data used in almost all your tests, use fixtures. For all the # other smaller scenarios, use factories. As both fixtures and factories # require valid attributes, this code below provides a quick solution # that allows you to create small, simple factories from the information # stored in your fixtures. # # == Examples # -
josevalim revised this gist
Oct 1, 2010 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -65,7 +65,9 @@ # end # ## Source code # Put it on test/supports/builders.rb and ensure it is required. # May be released as gem soon. module Builders @@builders = ActiveSupport::OrderedHash.new -
josevalim created this gist
Oct 1, 2010 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,128 @@ # A couple years ago the Rails community discovered the joys of factory # builders as an alternative to fixtures. Rails developers have had # bad experience with fixtures since a long time due to several reasons # including misuse. # # This misuse is often characterized by a huge ammount of factories, # causing a lot of data to maintain and dependence between tests. In my # experience working (and rescueing) different applications, 80% of these # factories are used only by 20% of tests (which are usually tests that # need a huge ammount of data to test different scenarios). # # Factory builders provides an alternative for you to easily build different # scenarios based on a commont set of factories. However, if you have # integration tests, creating the basic structure before each tests using # factories is definitely expensive. # # This is an attempt to have the best of both worlds by incentivating # you to use fixtures but providing helpers similar to factories built # on top of your fixtures. # # == Examples # # You can define your builder inside the Builders module: # # module Builders # build :message do # { :title => "OMG", :queue => queues(:general) } # end # end # # It should necessarily return a hash. After defining this builder, # you can easily create a new message calling +create_message+ or # +new_message+ in your tests. Both methods accepts an optional # options parameter that is merged into the given hash. # # == Reusing fixtures # # The great benefit of builders is that you can reuse your fixtures # attributes, avoiding duplication. An explicit way of doing it is: # # build :message do # messages(:fixture_one).attributes.merge( # :title => "Overwritten title" # ) # end # # However, Builders provide an implicit way of doing the same: # # build :message, :like => :fixture_one do # { :title => "Overwritten title" } # end # # == Just Ruby # # Since all Builders are defined inside the Builders module, without # a DSL on top of it, it allows us to use Ruby in case we need to do # something more complex, like supporting sequences. # # module Builders # @@sequence = 0 # # def sequence # @@sequence += 1 # end # end # ## Source code. May be released as gem soon. module Builders @@builders = ActiveSupport::OrderedHash.new def self.build(name, options={}, &block) klass = options[:as] || name.to_s.classify.constantize builder = if options[:like] lambda { send(name.to_s.pluralize, options[:like]).attributes.merge(block.call) } else block end @@builders[name] = [klass, builder] end def self.retrieve(scope, name, method, options) if builder = @@builders[name.to_sym] klass, block = builder hash = block.bind(scope).call.merge(options || {}) hash.delete("id") [klass, hash] else raise NoMethodError, "No builder #{name.inspect} for `#{method}'" end end def method_missing(method, *args, &block) case method.to_s when /(create|new)_(.*?)(!)?$/ klass, hash = Builders.retrieve(self, $2, method, args.first) object = klass.new object.send("attributes=", hash, false) object.send("save#{$3}") if $1 == "create" object when /valid_(.*?)_attributes$/ Builders.retrieve(self, $1, method, args.first)[1] else super end end ActiveSupport::TestCase.send :include, self end ## Some examples from a Real App™. module Builders build :profile, :like => :hugobarauna do { :username => "georgeguimaraes" } end build :user do { :email => "[email protected]", :password => "123456", :profile => new_profile } end end