/vendor/plugins/attribute_fu/test/vendor/plugins/shoulda/lib/shoulda/active_record_helpers.rb

https://github.com/GunioRobot/wnetpbcore · Ruby · 338 lines · 240 code · 15 blank · 83 comment · 4 complexity · 726669f43ae1aed8fb7c045a3892eb1d MD5 · raw file

  1. module ThoughtBot # :nodoc:
  2. module Shoulda # :nodoc:
  3. # = Macro test helpers for your active record models
  4. #
  5. # These helpers will test most of the validations and associations for your ActiveRecord models.
  6. #
  7. # class UserTest < Test::Unit::TestCase
  8. # should_require_attributes :name, :phone_number
  9. # should_not_allow_values_for :phone_number, "abcd", "1234"
  10. # should_allow_values_for :phone_number, "(123) 456-7890"
  11. #
  12. # should_protect_attributes :password
  13. #
  14. # should_have_one :profile
  15. # should_have_many :dogs
  16. # should_have_many :messes, :through => :dogs
  17. # should_belong_to :lover
  18. # end
  19. #
  20. # For all of these helpers, the last parameter may be a hash of options.
  21. #
  22. module ActiveRecord
  23. # Ensures that the model cannot be saved if one of the attributes listed is not present.
  24. # Requires an existing record.
  25. #
  26. # Options:
  27. # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  28. # Regexp or string. Default = <tt>/blank/</tt>
  29. #
  30. # Example:
  31. # should_require_attributes :name, :phone_number
  32. def should_require_attributes(*attributes)
  33. message = get_options!(attributes, :message)
  34. message ||= /blank/
  35. klass = model_class
  36. attributes.each do |attribute|
  37. should "require #{attribute} to be set" do
  38. object = klass.new
  39. assert !object.valid?, "#{klass.name} does not require #{attribute}."
  40. assert object.errors.on(attribute), "#{klass.name} does not require #{attribute}."
  41. assert_contains(object.errors.on(attribute), message)
  42. end
  43. end
  44. end
  45. # Ensures that the model cannot be saved if one of the attributes listed is not unique.
  46. # Requires an existing record
  47. #
  48. # Options:
  49. # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  50. # Regexp or string. Default = <tt>/taken/</tt>
  51. #
  52. # Example:
  53. # should_require_unique_attributes :keyword, :username
  54. def should_require_unique_attributes(*attributes)
  55. message, scope = get_options!(attributes, :message, :scoped_to)
  56. message ||= /taken/
  57. klass = model_class
  58. attributes.each do |attribute|
  59. attribute = attribute.to_sym
  60. should "require unique value for #{attribute}#{" scoped to #{scope}" if scope}" do
  61. assert existing = klass.find(:first), "Can't find first #{klass}"
  62. object = klass.new
  63. object.send(:"#{attribute}=", existing.send(attribute))
  64. if scope
  65. assert_respond_to object, :"#{scope}=", "#{klass.name} doesn't seem to have a #{scope} attribute."
  66. object.send(:"#{scope}=", existing.send(scope))
  67. end
  68. assert !object.valid?, "#{klass.name} does not require a unique value for #{attribute}."
  69. assert object.errors.on(attribute), "#{klass.name} does not require a unique value for #{attribute}."
  70. assert_contains(object.errors.on(attribute), message)
  71. if scope
  72. # Now test that the object is valid when changing the scoped attribute
  73. # TODO: actually find all values for scope and create a unique one.
  74. object.send(:"#{scope}=", existing.send(scope).nil? ? 1 : existing.send(scope).next)
  75. object.errors.clear
  76. object.valid?
  77. assert_does_not_contain(object.errors.on(attribute), message,
  78. "after :#{scope} set to #{object.send(scope.to_sym)}")
  79. end
  80. end
  81. end
  82. end
  83. # Ensures that the attribute cannot be set on update
  84. # Requires an existing record
  85. #
  86. # should_protect_attributes :password, :admin_flag
  87. def should_protect_attributes(*attributes)
  88. get_options!(attributes)
  89. klass = model_class
  90. attributes.each do |attribute|
  91. attribute = attribute.to_sym
  92. should "not allow #{attribute} to be changed by update" do
  93. assert object = klass.find(:first), "Can't find first #{klass}"
  94. value = object[attribute]
  95. # TODO: 1 may not be a valid value for the attribute (due to validations)
  96. assert object.update_attributes({ attribute => 1 }),
  97. "Cannot update #{klass} with { :#{attribute} => 1 }, #{object.errors.full_messages.to_sentence}"
  98. assert object.valid?, "#{klass} isn't valid after changing #{attribute}"
  99. assert_equal value, object[attribute], "Was able to change #{klass}##{attribute}"
  100. end
  101. end
  102. end
  103. # Ensures that the attribute cannot be set to the given values
  104. # Requires an existing record
  105. #
  106. # Options:
  107. # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  108. # Regexp or string. Default = <tt>/invalid/</tt>
  109. #
  110. # Example:
  111. # should_not_allow_values_for :isbn, "bad 1", "bad 2"
  112. def should_not_allow_values_for(attribute, *bad_values)
  113. message = get_options!(bad_values, :message)
  114. message ||= /invalid/
  115. klass = model_class
  116. bad_values.each do |v|
  117. should "not allow #{attribute} to be set to \"#{v}\"" do
  118. assert object = klass.find(:first), "Can't find first #{klass}"
  119. object.send("#{attribute}=", v)
  120. assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
  121. assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
  122. assert_contains(object.errors.on(attribute), message, "when set to \"#{v}\"")
  123. end
  124. end
  125. end
  126. # Ensures that the attribute can be set to the given values.
  127. # Requires an existing record
  128. #
  129. # Options:
  130. # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  131. # Regexp or string. Default = <tt>/invalid/</tt>
  132. #
  133. # Example:
  134. # should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
  135. def should_allow_values_for(attribute, *good_values)
  136. message = get_options!(good_values, :message)
  137. message ||= /invalid/
  138. klass = model_class
  139. good_values.each do |v|
  140. should "allow #{attribute} to be set to \"#{v}\"" do
  141. assert object = klass.find(:first), "Can't find first #{klass}"
  142. object.send("#{attribute}=", v)
  143. object.save
  144. assert_does_not_contain(object.errors.on(attribute), message, "when set to \"#{v}\"")
  145. end
  146. end
  147. end
  148. # Ensures that the length of the attribute is in the given range
  149. # Requires an existing record
  150. #
  151. # Options:
  152. # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  153. # Regexp or string. Default = <tt>/short/</tt>
  154. # * <tt>:long_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  155. # Regexp or string. Default = <tt>/long/</tt>
  156. #
  157. # Example:
  158. # should_ensure_length_in_range :password, (6..20)
  159. def should_ensure_length_in_range(attribute, range, opts = {})
  160. short_message, long_message = get_options!([opts], :short_message, :long_message)
  161. short_message ||= /short/
  162. long_message ||= /long/
  163. klass = model_class
  164. min_length = range.first
  165. max_length = range.last
  166. if min_length > 0
  167. min_value = "x" * (min_length - 1)
  168. should "not allow #{attribute} to be less than #{min_length} chars long" do
  169. assert object = klass.find(:first), "Can't find first #{klass}"
  170. object.send("#{attribute}=", min_value)
  171. assert !object.save, "Saved #{klass} with #{attribute} set to \"#{min_value}\""
  172. assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{min_value}\""
  173. assert_contains(object.errors.on(attribute), short_message, "when set to \"#{min_value}\"")
  174. end
  175. end
  176. max_value = "x" * (max_length + 1)
  177. should "not allow #{attribute} to be more than #{max_length} chars long" do
  178. assert object = klass.find(:first), "Can't find first #{klass}"
  179. object.send("#{attribute}=", max_value)
  180. assert !object.save, "Saved #{klass} with #{attribute} set to \"#{max_value}\""
  181. assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{max_value}\""
  182. assert_contains(object.errors.on(attribute), long_message, "when set to \"#{max_value}\"")
  183. end
  184. end
  185. # Ensure that the attribute is in the range specified
  186. # Requires an existing record
  187. #
  188. # Options:
  189. # * <tt>:low_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  190. # Regexp or string. Default = <tt>/included/</tt>
  191. # * <tt>:high_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  192. # Regexp or string. Default = <tt>/included/</tt>
  193. #
  194. # Example:
  195. # should_ensure_value_in_range :age, (0..100)
  196. def should_ensure_value_in_range(attribute, range, opts = {})
  197. low_message, high_message = get_options!([opts], :low_message, :high_message)
  198. low_message ||= /included/
  199. high_message ||= /included/
  200. klass = model_class
  201. min = range.first
  202. max = range.last
  203. should "not allow #{attribute} to be less than #{min}" do
  204. v = min - 1
  205. assert object = klass.find(:first), "Can't find first #{klass}"
  206. object.send("#{attribute}=", v)
  207. assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
  208. assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
  209. assert_contains(object.errors.on(attribute), low_message, "when set to \"#{v}\"")
  210. end
  211. should "not allow #{attribute} to be more than #{max}" do
  212. v = max + 1
  213. assert object = klass.find(:first), "Can't find first #{klass}"
  214. object.send("#{attribute}=", v)
  215. assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
  216. assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
  217. assert_contains(object.errors.on(attribute), high_message, "when set to \"#{v}\"")
  218. end
  219. end
  220. # Ensure that the attribute is numeric
  221. # Requires an existing record
  222. #
  223. # Options:
  224. # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
  225. # Regexp or string. Default = <tt>/number/</tt>
  226. #
  227. # Example:
  228. # should_only_allow_numeric_values_for :age
  229. def should_only_allow_numeric_values_for(*attributes)
  230. message = get_options!(attributes, :message)
  231. message ||= /number/
  232. klass = model_class
  233. attributes.each do |attribute|
  234. attribute = attribute.to_sym
  235. should "only allow numeric values for #{attribute}" do
  236. assert object = klass.find(:first), "Can't find first #{klass}"
  237. object.send(:"#{attribute}=", "abcd")
  238. assert !object.valid?, "Instance is still valid"
  239. assert_contains(object.errors.on(attribute), message)
  240. end
  241. end
  242. end
  243. # Ensures that the has_many relationship exists.
  244. #
  245. # Options:
  246. # * <tt>:through</tt> - association name for <tt>has_many :through</tt>
  247. #
  248. # Example:
  249. # should_have_many :friends
  250. # should_have_many :enemies, :through => :friends
  251. def should_have_many(*associations)
  252. through = get_options!(associations, :through)
  253. klass = model_class
  254. associations.each do |association|
  255. should "have many #{association}#{" through #{through}" if through}" do
  256. reflection = klass.reflect_on_association(association)
  257. assert reflection, "#{klass.name} does not have any relationship to #{association}"
  258. assert_equal :has_many, reflection.macro
  259. if through
  260. through_reflection = klass.reflect_on_association(through)
  261. assert through_reflection, "#{klass.name} does not have any relationship to #{through}"
  262. assert_equal(through, reflection.options[:through])
  263. end
  264. end
  265. end
  266. end
  267. # Ensures that the has_and_belongs_to_many relationship exists.
  268. #
  269. # should_have_and_belong_to_many :posts, :cars
  270. def should_have_and_belong_to_many(*associations)
  271. get_options!(associations)
  272. klass = model_class
  273. associations.each do |association|
  274. should "should have and belong to many #{association}" do
  275. assert klass.reflect_on_association(association), "#{klass.name} does not have any relationship to #{association}"
  276. assert_equal :has_and_belongs_to_many, klass.reflect_on_association(association).macro
  277. end
  278. end
  279. end
  280. # Ensure that the has_one relationship exists.
  281. #
  282. # should_have_one :god # unless hindu
  283. def should_have_one(*associations)
  284. get_options!(associations)
  285. klass = model_class
  286. associations.each do |association|
  287. should "have one #{association}" do
  288. assert klass.reflect_on_association(association), "#{klass.name} does not have any relationship to #{association}"
  289. assert_equal :has_one, klass.reflect_on_association(association).macro
  290. end
  291. end
  292. end
  293. # Ensure that the belongs_to relationship exists.
  294. #
  295. # should_belong_to :parent
  296. def should_belong_to(*associations)
  297. get_options!(associations)
  298. klass = model_class
  299. associations.each do |association|
  300. should "belong_to #{association}" do
  301. reflection = klass.reflect_on_association(association)
  302. assert reflection, "#{klass.name} does not have any relationship to #{association}"
  303. assert_equal :belongs_to, reflection.macro
  304. fk = reflection.options[:foreign_key] || "#{association}_id"
  305. assert klass.column_names.include?(fk), "#{klass.name} does not have a #{fk} foreign key."
  306. end
  307. end
  308. end
  309. private
  310. include ThoughtBot::Shoulda::Private
  311. end
  312. end
  313. end