Guest User

Untitled

a guest
Jun 25th, 2018
220
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.37 KB | None | 0 0
  1. # Rails developers have long had bad experiences with fixtures for
  2. # several reasons, including misuse.
  3. #
  4. # Misuse of fixtures is characterized by having a huge number of them,
  5. # requiring the developer to maintain a lot of data and creating dependencies
  6. # between tests. In my experience working (and rescuing) many applications, 80%
  7. # of fixtures are only used by 20% of tests.
  8. #
  9. # An example of such tests is one assuring that a given SQL query with
  10. # GROUP BY and ORDER BY conditions returns the correct result set. As expected,
  11. # a huge amount of data is needed for this test, most of which we won't be used
  12. # in other tests.
  13. #
  14. # For these scenarios factories are a fine solution. They won't clutter up
  15. # your database since they are created (and destroyed) during the execution
  16. # of specific tests and are easier to maintain as the underlying models change.
  17. #
  18. # I believe this was the primary reason for the Rails community to strongly
  19. # adopt factories builders over the few years.
  20. #
  21. # However, factories are also misused. Developers commonly create a huge
  22. # amount of data with factories before each test in an integration
  23. # suite, which causes their test suite to run slowly, where fixtures would
  24. # work great for this purpose.
  25. #
  26. # This is a small attempt to have the best of both worlds.
  27. #
  28. # For the data used in almost all your tests, simply use fixtures. For all the
  29. # other smaller scenarios, use factories. As both fixtures and factories
  30. # require valid attributes, this quick solution allows you to create small,
  31. # simple factories from the information stored in your fixtures.
  32. #
  33. # == Examples
  34. #
  35. # Define your builder inside the Builders module:
  36. #
  37. # module Builders
  38. # build :message do
  39. # { :title => "OMG", :queue => queues(:general) }
  40. # end
  41. # end
  42. #
  43. # The builder must return a hash. After defining this builder,
  44. # create a new message by calling +create_message+ or +new_message+
  45. # in your tests. Both methods accepts an optional options
  46. # parameter that gets merged into the given hash.
  47. #
  48. # == Reusing fixtures
  49. #
  50. # The great benefit of builders is that you can reuse your fixtures
  51. # attributes, avoiding duplication. An explicit way of doing it is:
  52. #
  53. # build :message do
  54. # messages(:fixture_one).attributes.merge(
  55. # :title => "Overwritten title"
  56. # )
  57. # end
  58. #
  59. # However, Builders provide an implicit way of doing the same:
  60. #
  61. # build :message, :like => :fixture_one do
  62. # { :title => "Overwritten title" }
  63. # end
  64. #
  65. # == Just Ruby
  66. #
  67. # Since all Builders are defined inside the Builders module, without
  68. # a DSL on top of it, we can use Ruby to meet more complex needs,
  69. # like supporting sequences.
  70. #
  71. # module Builders
  72. # @@sequence = 0
  73. #
  74. # def sequence
  75. # @@sequence += 1
  76. # end
  77. # end
  78. #
  79.  
  80. ## Source code
  81. # Put it on test/supports/builders.rb and ensure it is required.
  82. # May be released as gem soon.
  83.  
  84. module Builders
  85. @@builders = ActiveSupport::OrderedHash.new
  86.  
  87. def self.build(name, options={}, &block)
  88. klass = options[:as] || name.to_s.classify.constantize
  89.  
  90. builder = if options[:like]
  91. lambda { send(name.to_s.pluralize, options[:like]).attributes.merge(block.call) }
  92. else
  93. block
  94. end
  95.  
  96. @@builders[name] = [klass, builder]
  97. end
  98.  
  99. def self.retrieve(scope, name, method, options)
  100. if builder = @@builders[name.to_sym]
  101. klass, block = builder
  102. hash = block.bind(scope).call.merge(options || {})
  103. hash.delete("id")
  104. [klass, hash]
  105. else
  106. raise NoMethodError, "No builder #{name.inspect} for `#{method}'"
  107. end
  108. end
  109.  
  110. def method_missing(method, *args, &block)
  111. case method.to_s
  112. when /(create|new)_(.*?)(!)?$/
  113. klass, hash = Builders.retrieve(self, $2, method, args.first)
  114. object = klass.new
  115. object.send("attributes=", hash, false)
  116. object.send("save#{$3}") if $1 == "create"
  117. object
  118. when /valid_(.*?)_attributes$/
  119. Builders.retrieve(self, $1, method, args.first)[1]
  120. else
  121. super
  122. end
  123. end
  124.  
  125. ActiveSupport::TestCase.send :include, self
  126. end
  127.  
  128. ## Some examples from a Real App™.
  129.  
  130. module Builders
  131. build :profile, :like => :hugobarauna do
  132. { :username => "georgeguimaraes" }
  133. end
  134.  
  135. build :user do
  136. {
  137. :email => "george@example.com",
  138. :password => "123456",
  139. :profile => new_profile
  140. }
  141. end
  142. end
  143.  
  144. test "users sets profile gravatar on save" do
  145. user = create_user!
  146. assert_equal Digest::MD5.hexdigest("george@example.com"), user.profile.gravatar
  147. end
Add Comment
Please, Sign In to add comment