Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # 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
- #
- # 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, we can use Ruby to meet more complex needs,
- # like supporting sequences.
- #
- # module Builders
- # @@sequence = 0
- #
- # def sequence
- # @@sequence += 1
- # end
- # 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
- 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 => "george@example.com",
- :password => "123456",
- :profile => new_profile
- }
- end
- end
- test "users sets profile gravatar on save" do
- user = create_user!
- assert_equal Digest::MD5.hexdigest("george@example.com"), user.profile.gravatar
- end
Add Comment
Please, Sign In to add comment