francois-shoulda 2.0.5.4 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/README.rdoc +60 -10
  2. data/Rakefile +7 -7
  3. data/lib/shoulda.rb +7 -15
  4. data/lib/shoulda/action_controller.rb +28 -0
  5. data/lib/shoulda/action_controller/helpers.rb +47 -0
  6. data/lib/shoulda/action_controller/macros.rb +277 -0
  7. data/lib/shoulda/action_controller/matchers.rb +37 -0
  8. data/lib/shoulda/action_controller/matchers/assign_to_matcher.rb +109 -0
  9. data/lib/shoulda/action_controller/matchers/filter_param_matcher.rb +57 -0
  10. data/lib/shoulda/action_controller/matchers/render_with_layout_matcher.rb +81 -0
  11. data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +70 -0
  12. data/lib/shoulda/action_controller/matchers/respond_with_matcher.rb +81 -0
  13. data/lib/shoulda/action_controller/matchers/route_matcher.rb +93 -0
  14. data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +87 -0
  15. data/lib/shoulda/action_controller/matchers/set_the_flash_matcher.rb +85 -0
  16. data/lib/shoulda/action_mailer.rb +1 -1
  17. data/lib/shoulda/action_mailer/assertions.rb +32 -33
  18. data/lib/shoulda/action_view.rb +10 -0
  19. data/lib/shoulda/action_view/macros.rb +56 -0
  20. data/lib/shoulda/active_record.rb +6 -2
  21. data/lib/shoulda/active_record/assertions.rb +62 -89
  22. data/lib/shoulda/active_record/helpers.rb +40 -0
  23. data/lib/shoulda/active_record/macros.rb +520 -684
  24. data/lib/shoulda/active_record/matchers.rb +42 -0
  25. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
  26. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
  27. data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
  28. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
  29. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
  30. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
  31. data/lib/shoulda/active_record/matchers/have_index_matcher.rb +105 -0
  32. data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +125 -0
  33. data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
  34. data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
  35. data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
  36. data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
  37. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
  38. data/lib/shoulda/active_record/matchers/validation_matcher.rb +56 -0
  39. data/lib/shoulda/assertions.rb +50 -40
  40. data/lib/shoulda/autoload_macros.rb +46 -0
  41. data/lib/shoulda/context.rb +124 -126
  42. data/lib/shoulda/helpers.rb +5 -7
  43. data/lib/shoulda/macros.rb +63 -64
  44. data/lib/shoulda/private_helpers.rb +16 -18
  45. data/lib/shoulda/rails.rb +5 -11
  46. data/lib/shoulda/rspec.rb +11 -0
  47. data/lib/shoulda/tasks/list_tests.rake +6 -1
  48. data/lib/shoulda/test_unit.rb +19 -0
  49. data/rails/init.rb +7 -1
  50. data/test/README +2 -2
  51. data/test/fail_macros.rb +15 -15
  52. data/test/fixtures/tags.yml +1 -1
  53. data/test/functional/posts_controller_test.rb +46 -26
  54. data/test/functional/users_controller_test.rb +0 -19
  55. data/test/matchers/active_record/allow_mass_assignment_of_matcher_test.rb +68 -0
  56. data/test/matchers/active_record/allow_value_matcher_test.rb +41 -0
  57. data/test/matchers/active_record/association_matcher_test.rb +258 -0
  58. data/test/matchers/active_record/ensure_inclusion_of_matcher_test.rb +80 -0
  59. data/test/matchers/active_record/ensure_length_of_matcher_test.rb +158 -0
  60. data/test/matchers/active_record/have_db_column_matcher_test.rb +169 -0
  61. data/test/matchers/active_record/have_index_matcher_test.rb +74 -0
  62. data/test/matchers/active_record/have_named_scope_matcher_test.rb +65 -0
  63. data/test/matchers/active_record/have_readonly_attributes_matcher_test.rb +29 -0
  64. data/test/matchers/active_record/validate_acceptance_of_matcher_test.rb +44 -0
  65. data/test/matchers/active_record/validate_numericality_of_matcher_test.rb +52 -0
  66. data/test/matchers/active_record/validate_presence_of_matcher_test.rb +86 -0
  67. data/test/matchers/active_record/validate_uniqueness_of_matcher_test.rb +147 -0
  68. data/test/matchers/controller/assign_to_matcher_test.rb +35 -0
  69. data/test/matchers/controller/filter_param_matcher_test.rb +32 -0
  70. data/test/matchers/controller/render_with_layout_matcher_test.rb +33 -0
  71. data/test/matchers/controller/respond_with_content_type_matcher_test.rb +27 -0
  72. data/test/matchers/controller/respond_with_matcher_test.rb +106 -0
  73. data/test/matchers/controller/route_matcher_test.rb +58 -0
  74. data/test/matchers/controller/set_session_matcher_test.rb +31 -0
  75. data/test/matchers/controller/set_the_flash_matcher.rb +41 -0
  76. data/test/model_builder.rb +106 -0
  77. data/test/other/autoload_macro_test.rb +18 -0
  78. data/test/other/helpers_test.rb +58 -0
  79. data/test/other/private_helpers_test.rb +1 -1
  80. data/test/other/should_test.rb +16 -16
  81. data/test/rails_root/app/controllers/posts_controller.rb +6 -5
  82. data/test/rails_root/app/models/pets/dog.rb +10 -0
  83. data/test/rails_root/app/models/treat.rb +3 -0
  84. data/test/rails_root/app/models/user.rb +4 -3
  85. data/test/rails_root/app/views/layouts/posts.rhtml +2 -0
  86. data/test/rails_root/config/database.yml +1 -1
  87. data/test/rails_root/config/environment.rb +1 -1
  88. data/test/rails_root/config/environments/{sqlite3.rb → test.rb} +0 -0
  89. data/test/rails_root/db/migrate/001_create_users.rb +3 -2
  90. data/test/rails_root/db/migrate/011_create_treats.rb +12 -0
  91. data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
  92. data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
  93. data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
  94. data/test/rspec_test.rb +207 -0
  95. data/test/test_helper.rb +3 -1
  96. data/test/unit/address_test.rb +1 -23
  97. data/test/unit/dog_test.rb +5 -2
  98. data/test/unit/post_test.rb +7 -3
  99. data/test/unit/product_test.rb +2 -2
  100. data/test/unit/tag_test.rb +2 -1
  101. data/test/unit/user_test.rb +25 -9
  102. metadata +84 -23
  103. data/lib/shoulda/controller.rb +0 -30
  104. data/lib/shoulda/controller/formats/html.rb +0 -201
  105. data/lib/shoulda/controller/formats/xml.rb +0 -170
  106. data/lib/shoulda/controller/helpers.rb +0 -64
  107. data/lib/shoulda/controller/macros.rb +0 -316
  108. data/lib/shoulda/controller/resource_options.rb +0 -236
  109. data/test/rails_root/app/models/dog.rb +0 -5
@@ -0,0 +1,40 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Helpers
4
+ def pretty_error_messages(obj) # :nodoc:
5
+ obj.errors.map do |a, m|
6
+ msg = "#{a} #{m}"
7
+ msg << " (#{obj.send(a).inspect})" unless a.to_sym == :base
8
+ end
9
+ end
10
+
11
+ def get_instance_of(object_or_klass)
12
+ if object_or_klass.is_a?(Class)
13
+ klass = object_or_klass
14
+ instance_variable_get("@#{instance_variable_name_for(klass)}") || klass.new
15
+ else
16
+ object_or_klass
17
+ end
18
+ end
19
+
20
+ def instance_variable_name_for(klass)
21
+ klass.to_s.split('::').last.underscore
22
+ end
23
+
24
+ # Helper method that determines the default error message used by Active
25
+ # Record. Works for both existing Rails 2.1 and Rails 2.2 with the newly
26
+ # introduced I18n module used for localization.
27
+ #
28
+ # default_error_message(:blank)
29
+ # default_error_message(:too_short, :count => 5)
30
+ # default_error_message(:too_long, :count => 60)
31
+ def default_error_message(key, values = {})
32
+ if Object.const_defined?(:I18n) # Rails >= 2.2
33
+ I18n.translate("activerecord.errors.messages.#{key}", values)
34
+ else # Rails <= 2.1.x
35
+ ::ActiveRecord::Errors.default_error_messages[key] % values[:count]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,763 +1,599 @@
1
- module ThoughtBot # :nodoc:
2
- module Shoulda # :nodoc:
3
- module ActiveRecord # :nodoc:
4
- module MacroHelpers # :nodoc:
5
- # Helper method that determines the default error message used by Active
6
- # Record. Works for both existing Rails 2.1 and Rails 2.2 with the newly
7
- # introduced I18n module used for localization.
8
- #
9
- # default_error_message(:blank)
10
- # default_error_message(:too_short, :count => 5)
11
- # default_error_message(:too_long, :count => 60)
12
- def default_error_message(key, values = {})
13
- if Object.const_defined?(:I18n) # Rails >= 2.2
14
- I18n.translate("activerecord.errors.messages.#{key}", values)
15
- else # Rails <= 2.1.x
16
- ::ActiveRecord::Errors.default_error_messages[key] % values[:count]
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :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_validate_presence_of :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_not_allow_mass_assignment_of :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 Macros
23
+ include Helpers
24
+ include Matchers
25
+
26
+ # Validates that all fixtures of the current class are valid.
27
+ #
28
+ # Example:
29
+ # should_have_valid_fixtures
30
+ def should_have_valid_fixtures
31
+ klass = model_class
32
+ should "have valid fixtures" do
33
+ klass.all.each do |object|
34
+ assert_valid object
17
35
  end
18
36
  end
19
37
  end
20
38
 
21
- # = Macro test helpers for your active record models
39
+ # Ensures that the model cannot be saved if one of the attributes listed is not present.
22
40
  #
23
- # These helpers will test most of the validations and associations for your ActiveRecord models.
41
+ # If an instance variable has been created in the setup named after the
42
+ # model being tested, then this method will use that. Otherwise, it will
43
+ # create a new instance to test against.
24
44
  #
25
- # class UserTest < Test::Unit::TestCase
26
- # should_require_attributes :name, :phone_number
27
- # should_not_allow_values_for :phone_number, "abcd", "1234"
28
- # should_allow_values_for :phone_number, "(123) 456-7890"
45
+ # Options:
46
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
47
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.blank')</tt>
29
48
  #
30
- # should_protect_attributes :password
49
+ # Example:
50
+ # should_validate_presence_of :name, :phone_number
31
51
  #
32
- # should_have_one :profile
33
- # should_have_many :dogs
34
- # should_have_many :messes, :through => :dogs
35
- # should_belong_to :lover
36
- # end
52
+ def should_validate_presence_of(*attributes)
53
+ message = get_options!(attributes, :message)
54
+ klass = model_class
55
+
56
+ attributes.each do |attribute|
57
+ matcher = validate_presence_of(attribute).with_message(message)
58
+ should matcher.description do
59
+ assert_accepts(matcher, get_instance_of(klass))
60
+ end
61
+ end
62
+ end
63
+
64
+ # Deprecated. See should_validate_presence_of
65
+ def should_require_attributes(*attributes)
66
+ warn "[DEPRECATION] should_require_attributes is deprecated. " <<
67
+ "Use should_validate_presence_of instead."
68
+ should_validate_presence_of(*attributes)
69
+ end
70
+
71
+ # Ensures that the model cannot be saved if one of the attributes listed is not unique.
72
+ # Requires an existing record
73
+ #
74
+ # Options:
75
+
76
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
77
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.taken')</tt>
78
+ # * <tt>:scoped_to</tt> - field(s) to scope the uniqueness to.
79
+ # * <tt>:case_sensitive</tt> - whether or not uniqueness is defined by an
80
+ # exact match. Ignored by non-text attributes. Default = <tt>true</tt>
37
81
  #
38
- # For all of these helpers, the last parameter may be a hash of options.
82
+ # Examples:
83
+ # should_validate_uniqueness_of :keyword, :username
84
+ # should_validate_uniqueness_of :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
85
+ # should_validate_uniqueness_of :email, :scoped_to => :name
86
+ # should_validate_uniqueness_of :address, :scoped_to => [:first_name, :last_name]
87
+ # should_validate_uniqueness_of :email, :case_sensitive => false
39
88
  #
40
- module Macros
41
- include MacroHelpers
89
+ def should_validate_uniqueness_of(*attributes)
90
+ message, scope, case_sensitive = get_options!(attributes, :message, :scoped_to, :case_sensitive)
91
+ scope = [*scope].compact
92
+ case_sensitive = true if case_sensitive.nil?
42
93
 
43
- # <b>DEPRECATED:</b> Use <tt>fixtures :all</tt> instead
44
- #
45
- # Loads all fixture files (<tt>test/fixtures/*.yml</tt>)
46
- def load_all_fixtures
47
- warn "[DEPRECATION] load_all_fixtures is deprecated. Use `fixtures :all` instead."
48
- fixtures :all
49
- end
94
+ klass = model_class
50
95
 
51
- # Validates that all fixtures of the current class are valid.
52
- #
53
- # Example:
54
- # should_have_valid_fixtures
55
- def should_have_valid_fixtures
56
- klass = model_class
57
- should "have valid fixtures" do
58
- klass.all.each do |object|
59
- valid = object.valid?
60
- assert valid, "Fixture #{object.inspect} is invalid: #{object.errors.full_messages}"
61
- end
96
+ attributes.each do |attribute|
97
+ matcher = validate_uniqueness_of(attribute).
98
+ with_message(message).scoped_to(scope)
99
+ matcher = matcher.case_insensitive unless case_sensitive
100
+ should matcher.description do
101
+ assert_accepts(matcher, get_instance_of(klass))
62
102
  end
63
103
  end
104
+ end
64
105
 
65
- # Ensures that the model cannot be saved if one of the attributes listed is not present.
66
- #
67
- # If an instance variable has been created in the setup named after the
68
- # model being tested, then this method will use that. Otherwise, it will
69
- # create a new instance to test against.
70
- #
71
- # Options:
72
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
73
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.blank')</tt>
74
- #
75
- # Example:
76
- # should_require_attributes :name, :phone_number
77
- #
78
- def should_require_attributes(*attributes)
79
- message = get_options!(attributes, :message)
80
- message ||= default_error_message(:blank)
81
- klass = model_class
82
-
83
- attributes.each do |attribute|
84
- should "require #{attribute} to be set" do
85
- assert_bad_value(klass, attribute, nil, message)
86
- end
87
- end
88
- end
106
+ # Deprecated. See should_validate_uniqueness_of
107
+ def should_require_unique_attributes(*attributes)
108
+ warn "[DEPRECATION] should_require_unique_attributes is deprecated. " <<
109
+ "Use should_validate_uniqueness_of instead."
110
+ should_validate_uniqueness_of(*attributes)
111
+ end
89
112
 
90
- # Ensures that the model cannot be saved if one of the attributes listed is not unique.
91
- # Requires an existing record
92
- #
93
- # Options:
94
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
95
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.taken')</tt>
96
- # * <tt>:scoped_to</tt> - field(s) to scope the uniqueness to.
97
- #
98
- # Examples:
99
- # should_require_unique_attributes :keyword, :username
100
- # should_require_unique_attributes :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
101
- # should_require_unique_attributes :email, :scoped_to => :name
102
- # should_require_unique_attributes :address, :scoped_to => [:first_name, :last_name]
103
- #
104
- def should_require_unique_attributes(*attributes)
105
- message, scope = get_options!(attributes, :message, :scoped_to)
106
- scope = [*scope].compact
107
- message ||= default_error_message(:taken)
108
-
109
- klass = model_class
110
- attributes.each do |attribute|
111
- attribute = attribute.to_sym
112
- should "require unique value for #{attribute}#{" scoped to #{scope.join(', ')}" unless scope.blank?}" do
113
- assert existing = klass.find(:first), "Can't find first #{klass}"
114
- object = klass.new
115
- existing_value = existing.send(attribute)
116
-
117
- if !scope.blank?
118
- scope.each do |s|
119
- assert_respond_to object, :"#{s}=", "#{klass.name} doesn't seem to have a #{s} attribute."
120
- object.send("#{s}=", existing.send(s))
121
- end
122
- end
123
- assert_bad_value(object, attribute, existing_value, message)
124
-
125
- # Now test that the object is valid when changing the scoped attribute
126
- # TODO: There is a chance that we could change the scoped field
127
- # to a value that's already taken. An alternative implementation
128
- # could actually find all values for scope and create a unique
129
- # one.
130
- if !scope.blank?
131
- scope.each do |s|
132
- # Assume the scope is a foreign key if the field is nil
133
- object.send("#{s}=", existing.send(s).nil? ? 1 : existing.send(s).next)
134
- assert_good_value(object, attribute, existing_value, message)
135
- end
136
- end
137
- end
138
- end
139
- end
113
+ # Ensures that the attribute can be set on mass update.
114
+ #
115
+ # should_allow_mass_assignment_of :first_name, :last_name
116
+ #
117
+ def should_allow_mass_assignment_of(*attributes)
118
+ get_options!(attributes)
119
+ klass = model_class
140
120
 
141
- # Ensures that the attribute cannot be set on mass update.
142
- #
143
- # should_not_allow_mass_assignment_of :password, :admin_flag
144
- #
145
- def should_not_allow_mass_assignment_of(*attributes)
146
- get_options!(attributes)
147
- klass = model_class
148
-
149
- attributes.each do |attribute|
150
- attribute = attribute.to_sym
151
- should "protect #{attribute} from mass updates" do
152
- protected = klass.protected_attributes || []
153
- accessible = klass.accessible_attributes || []
154
-
155
- assert protected.include?(attribute.to_s) ||
156
- (!accessible.empty? && !accessible.include?(attribute.to_s)),
157
- (accessible.empty? ?
158
- "#{klass} is protecting #{protected.to_a.to_sentence}, but not #{attribute}." :
159
- "#{klass} has made #{attribute} accessible")
160
- end
121
+ attributes.each do |attribute|
122
+ matcher = allow_mass_assignment_of(attribute)
123
+ should matcher.description do
124
+ assert_accepts matcher, klass.new
161
125
  end
162
126
  end
127
+ end
163
128
 
164
- def should_protect_attributes(*attributes)
165
- warn "[DEPRECATION] should_protect_attributes is deprecated; use should_not_allow_mass_assignment_of. Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/106-should_allow_attributes for more information."
166
- should_not_allow_mass_assignment_of(*attributes)
167
- end
129
+ # Ensures that the attribute cannot be set on mass update.
130
+ #
131
+ # should_not_allow_mass_assignment_of :password, :admin_flag
132
+ #
133
+ def should_not_allow_mass_assignment_of(*attributes)
134
+ get_options!(attributes)
135
+ klass = model_class
168
136
 
169
- # Ensures that the attribute can be set on mass update.
170
- #
171
- # should_allow_mass_assignment_of :name, :email
172
- #
173
- def should_allow_mass_assignment_of(*attributes)
174
- get_options!(attributes)
175
- klass = model_class
176
-
177
- attributes.each do |attribute|
178
- attribute = attribute.to_sym
179
- should "allow #{attribute} from mass updates" do
180
- protected = klass.protected_attributes || []
181
- accessible = klass.accessible_attributes || []
182
-
183
- assert accessible.include?(attribute.to_s) ||
184
- (!protected.empty? && !protected.include?(attribute.to_s)),
185
- (protected.empty? ?
186
- "#{klass} is allowing #{accessible.to_a.to_sentence}, but not #{attribute}." :
187
- "#{klass} has made #{attribute} protected")
188
- end
137
+ attributes.each do |attribute|
138
+ matcher = allow_mass_assignment_of(attribute)
139
+ should "not #{matcher.description}" do
140
+ assert_rejects matcher, klass.new
189
141
  end
190
142
  end
143
+ end
191
144
 
192
- # <b>DEPRECATED:</b> Please see
193
- # http://thoughtbot.lighthouseapp.com/projects/5807/tickets/106-should_allow_attributes for more
194
- # information.
195
- def should_allow_attributes(*attributes)
196
- warn "[DEPRECATION] should_allow_attributes is deprecated; use should_allow_mass_assignment_of. Please see http://thoughtbot.lighthouseapp.com/projects/5807/tickets/106-should_allow_attributes for more information."
197
- should_allow_mass_assignment_of(*attributes)
198
- end
145
+ # Deprecated. See should_not_allow_mass_assignment_of
146
+ def should_protect_attributes(*attributes)
147
+ warn "[DEPRECATION] should_protect_attributes is deprecated. " <<
148
+ "Use should_not_allow_mass_assignment_of instead."
149
+ should_not_allow_mass_assignment_of(*attributes)
150
+ end
199
151
 
200
- # Ensures that the attribute cannot be changed once the record has been created.
201
- #
202
- # should_have_readonly_attributes :password, :admin_flag
203
- #
204
- def should_have_readonly_attributes(*attributes)
205
- get_options!(attributes)
206
- klass = model_class
207
-
208
- attributes.each do |attribute|
209
- attribute = attribute.to_sym
210
- should "make #{attribute} read-only" do
211
- readonly = klass.readonly_attributes || []
212
-
213
- assert readonly.include?(attribute.to_s),
214
- (readonly.empty? ?
215
- "#{klass} attribute #{attribute} is not read-only" :
216
- "#{klass} is making #{readonly.to_a.to_sentence} read-only, but not #{attribute}.")
217
- end
152
+ # Ensures that the attribute cannot be changed once the record has been created.
153
+ #
154
+ # should_have_readonly_attributes :password, :admin_flag
155
+ #
156
+ def should_have_readonly_attributes(*attributes)
157
+ get_options!(attributes)
158
+ klass = model_class
159
+
160
+ attributes.each do |attribute|
161
+ matcher = have_readonly_attribute(attribute)
162
+ should matcher.description do
163
+ assert_accepts matcher, klass.new
218
164
  end
219
165
  end
166
+ end
220
167
 
221
- # Ensures that the attribute cannot be set to the given values
222
- #
223
- # If an instance variable has been created in the setup named after the
224
- # model being tested, then this method will use that. Otherwise, it will
225
- # create a new instance to test against.
226
- #
227
- # Options:
228
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
229
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.invalid')</tt>
230
- #
231
- # Example:
232
- # should_not_allow_values_for :isbn, "bad 1", "bad 2"
233
- #
234
- def should_not_allow_values_for(attribute, *bad_values)
235
- message = get_options!(bad_values, :message)
236
- message ||= default_error_message(:invalid)
237
- klass = model_class
238
- bad_values.each do |v|
239
- should "not allow #{attribute} to be set to #{v.inspect}" do
240
- assert_bad_value(klass, attribute, v, message)
241
- end
168
+ # Ensures that the attribute cannot be set to the given values
169
+ #
170
+ # If an instance variable has been created in the setup named after the
171
+ # model being tested, then this method will use that. Otherwise, it will
172
+ # create a new instance to test against.
173
+ #
174
+ # Options:
175
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
176
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.invalid')</tt>
177
+ #
178
+ # Example:
179
+ # should_not_allow_values_for :isbn, "bad 1", "bad 2"
180
+ #
181
+ def should_not_allow_values_for(attribute, *bad_values)
182
+ message = get_options!(bad_values, :message)
183
+ klass = model_class
184
+ bad_values.each do |value|
185
+ matcher = allow_value(value).for(attribute).with_message(message)
186
+ should "not #{matcher.description}" do
187
+ assert_rejects matcher, get_instance_of(klass)
242
188
  end
243
189
  end
190
+ end
244
191
 
245
- # Ensures that the attribute can be set to the given values.
246
- #
247
- # If an instance variable has been created in the setup named after the
248
- # model being tested, then this method will use that. Otherwise, it will
249
- # create a new instance to test against.
250
- #
251
- # Example:
252
- # should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
253
- #
254
- def should_allow_values_for(attribute, *good_values)
255
- get_options!(good_values)
256
- klass = model_class
257
- good_values.each do |v|
258
- should "allow #{attribute} to be set to #{v.inspect}" do
259
- assert_good_value(klass, attribute, v)
260
- end
192
+ # Ensures that the attribute can be set to the given values.
193
+ #
194
+ # If an instance variable has been created in the setup named after the
195
+ # model being tested, then this method will use that. Otherwise, it will
196
+ # create a new instance to test against.
197
+ #
198
+ # Example:
199
+ # should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
200
+ #
201
+ def should_allow_values_for(attribute, *good_values)
202
+ get_options!(good_values)
203
+ klass = model_class
204
+ good_values.each do |value|
205
+ matcher = allow_value(value).for(attribute)
206
+ should matcher.description do
207
+ assert_accepts matcher, get_instance_of(klass)
261
208
  end
262
209
  end
210
+ end
263
211
 
264
- # Ensures that the length of the attribute is in the given range
265
- #
266
- # If an instance variable has been created in the setup named after the
267
- # model being tested, then this method will use that. Otherwise, it will
268
- # create a new instance to test against.
269
- #
270
- # Options:
271
- # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
272
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % range.first</tt>
273
- # * <tt>:long_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
274
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_long') % range.last</tt>
275
- #
276
- # Example:
277
- # should_ensure_length_in_range :password, (6..20)
278
- #
279
- def should_ensure_length_in_range(attribute, range, opts = {})
280
- short_message, long_message = get_options!([opts], :short_message, :long_message)
281
- short_message ||= default_error_message(:too_short, :count => range.first)
282
- long_message ||= default_error_message(:too_long, :count => range.last)
283
-
284
- klass = model_class
285
- min_length = range.first
286
- max_length = range.last
287
- same_length = (min_length == max_length)
288
-
289
- if min_length > 0
290
- should "not allow #{attribute} to be less than #{min_length} chars long" do
291
- min_value = "x" * (min_length - 1)
292
- assert_bad_value(klass, attribute, min_value, short_message)
293
- end
294
- end
212
+ # Ensures that the length of the attribute is in the given range
213
+ #
214
+ # If an instance variable has been created in the setup named after the
215
+ # model being tested, then this method will use that. Otherwise, it will
216
+ # create a new instance to test against.
217
+ #
218
+ # Options:
219
+ # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
220
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % range.first</tt>
221
+ # * <tt>:long_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
222
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_long') % range.last</tt>
223
+ #
224
+ # Example:
225
+ # should_ensure_length_in_range :password, (6..20)
226
+ #
227
+ def should_ensure_length_in_range(attribute, range, opts = {})
228
+ short_message, long_message = get_options!([opts],
229
+ :short_message,
230
+ :long_message)
231
+ klass = model_class
232
+
233
+ matcher = ensure_length_of(attribute).
234
+ is_at_least(range.first).
235
+ with_short_message(short_message).
236
+ is_at_most(range.last).
237
+ with_long_message(long_message)
238
+
239
+ should matcher.description do
240
+ assert_accepts matcher, get_instance_of(klass)
241
+ end
242
+ end
295
243
 
296
- if min_length > 0
297
- should "allow #{attribute} to be exactly #{min_length} chars long" do
298
- min_value = "x" * min_length
299
- assert_good_value(klass, attribute, min_value, short_message)
300
- end
301
- end
244
+ # Ensures that the length of the attribute is at least a certain length
245
+ #
246
+ # If an instance variable has been created in the setup named after the
247
+ # model being tested, then this method will use that. Otherwise, it will
248
+ # create a new instance to test against.
249
+ #
250
+ # Options:
251
+ # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
252
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % min_length</tt>
253
+ #
254
+ # Example:
255
+ # should_ensure_length_at_least :name, 3
256
+ #
257
+ def should_ensure_length_at_least(attribute, min_length, opts = {})
258
+ short_message = get_options!([opts], :short_message)
259
+ klass = model_class
302
260
 
303
- should "not allow #{attribute} to be more than #{max_length} chars long" do
304
- max_value = "x" * (max_length + 1)
305
- assert_bad_value(klass, attribute, max_value, long_message)
306
- end
261
+ matcher = ensure_length_of(attribute).
262
+ is_at_least(min_length).
263
+ with_short_message(short_message)
307
264
 
308
- unless same_length
309
- should "allow #{attribute} to be exactly #{max_length} chars long" do
310
- max_value = "x" * max_length
311
- assert_good_value(klass, attribute, max_value, long_message)
312
- end
313
- end
265
+ should matcher.description do
266
+ assert_accepts matcher, get_instance_of(klass)
314
267
  end
268
+ end
315
269
 
316
- # Ensures that the length of the attribute is at least a certain length
317
- #
318
- # If an instance variable has been created in the setup named after the
319
- # model being tested, then this method will use that. Otherwise, it will
320
- # create a new instance to test against.
321
- #
322
- # Options:
323
- # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
324
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % min_length</tt>
325
- #
326
- # Example:
327
- # should_ensure_length_at_least :name, 3
328
- #
329
- def should_ensure_length_at_least(attribute, min_length, opts = {})
330
- short_message = get_options!([opts], :short_message)
331
- short_message ||= default_error_message(:too_short, :count => min_length)
332
-
333
- klass = model_class
334
-
335
- if min_length > 0
336
- min_value = "x" * (min_length - 1)
337
- should "not allow #{attribute} to be less than #{min_length} chars long" do
338
- assert_bad_value(klass, attribute, min_value, short_message)
339
- end
340
- end
341
- should "allow #{attribute} to be at least #{min_length} chars long" do
342
- valid_value = "x" * (min_length)
343
- assert_good_value(klass, attribute, valid_value, short_message)
344
- end
270
+ # Ensures that the length of the attribute is exactly a certain length
271
+ #
272
+ # If an instance variable has been created in the setup named after the
273
+ # model being tested, then this method will use that. Otherwise, it will
274
+ # create a new instance to test against.
275
+ #
276
+ # Options:
277
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
278
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.wrong_length') % length</tt>
279
+ #
280
+ # Example:
281
+ # should_ensure_length_is :ssn, 9
282
+ #
283
+ def should_ensure_length_is(attribute, length, opts = {})
284
+ message = get_options!([opts], :message)
285
+ klass = model_class
286
+ matcher = ensure_length_of(attribute).
287
+ is_equal_to(length).
288
+ with_message(message)
289
+
290
+ should matcher.description do
291
+ assert_accepts matcher, get_instance_of(klass)
345
292
  end
293
+ end
346
294
 
347
- # Ensures that the length of the attribute is exactly a certain length
348
- #
349
- # If an instance variable has been created in the setup named after the
350
- # model being tested, then this method will use that. Otherwise, it will
351
- # create a new instance to test against.
352
- #
353
- # Options:
354
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
355
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.wrong_length') % length</tt>
356
- #
357
- # Example:
358
- # should_ensure_length_is :ssn, 9
359
- #
360
- def should_ensure_length_is(attribute, length, opts = {})
361
- message = get_options!([opts], :message)
362
- message ||= default_error_message(:wrong_length, :count => length)
363
-
364
- klass = model_class
365
-
366
- should "not allow #{attribute} to be less than #{length} chars long" do
367
- min_value = "x" * (length - 1)
368
- assert_bad_value(klass, attribute, min_value, message)
369
- end
370
-
371
- should "not allow #{attribute} to be greater than #{length} chars long" do
372
- max_value = "x" * (length + 1)
373
- assert_bad_value(klass, attribute, max_value, message)
374
- end
375
-
376
- should "allow #{attribute} to be #{length} chars long" do
377
- valid_value = "x" * (length)
378
- assert_good_value(klass, attribute, valid_value, message)
379
- end
295
+ # Ensure that the attribute is in the range specified
296
+ #
297
+ # If an instance variable has been created in the setup named after the
298
+ # model being tested, then this method will use that. Otherwise, it will
299
+ # create a new instance to test against.
300
+ #
301
+ # Options:
302
+ # * <tt>:low_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
303
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.inclusion')</tt>
304
+ # * <tt>:high_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
305
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.inclusion')</tt>
306
+ #
307
+ # Example:
308
+ # should_ensure_value_in_range :age, (0..100)
309
+ #
310
+ def should_ensure_value_in_range(attribute, range, opts = {})
311
+ message, low_message, high_message = get_options!([opts],
312
+ :message,
313
+ :low_message,
314
+ :high_message)
315
+ klass = model_class
316
+ matcher = ensure_inclusion_of(attribute).
317
+ in_range(range).
318
+ with_message(message).
319
+ with_low_message(low_message).
320
+ with_high_message(high_message)
321
+ should matcher.description do
322
+ assert_accepts matcher, get_instance_of(klass)
380
323
  end
324
+ end
381
325
 
382
- # Ensure that the attribute is in the range specified
383
- #
384
- # If an instance variable has been created in the setup named after the
385
- # model being tested, then this method will use that. Otherwise, it will
386
- # create a new instance to test against.
387
- #
388
- # Options:
389
- # * <tt>:low_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
390
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.inclusion')</tt>
391
- # * <tt>:high_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
392
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.inclusion')</tt>
393
- #
394
- # Example:
395
- # should_ensure_value_in_range :age, (0..100)
396
- #
397
- def should_ensure_value_in_range(attribute, range, opts = {})
398
- low_message, high_message = get_options!([opts], :low_message, :high_message)
399
- low_message ||= default_error_message(:inclusion)
400
- high_message ||= default_error_message(:inclusion)
401
-
402
- klass = model_class
403
- min = range.first
404
- max = range.last
405
-
406
- should "not allow #{attribute} to be less than #{min}" do
407
- v = min - 1
408
- assert_bad_value(klass, attribute, v, low_message)
409
- end
410
-
411
- should "allow #{attribute} to be #{min}" do
412
- v = min
413
- assert_good_value(klass, attribute, v, low_message)
326
+ # Ensure that the attribute is numeric
327
+ #
328
+ # If an instance variable has been created in the setup named after the
329
+ # model being tested, then this method will use that. Otherwise, it will
330
+ # create a new instance to test against.
331
+ #
332
+ # Options:
333
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
334
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.not_a_number')</tt>
335
+ #
336
+ # Example:
337
+ # should_validate_numericality_of :age
338
+ #
339
+ def should_validate_numericality_of(*attributes)
340
+ message = get_options!(attributes, :message)
341
+ klass = model_class
342
+ attributes.each do |attribute|
343
+ matcher = validate_numericality_of(attribute).
344
+ with_message(message)
345
+ should matcher.description do
346
+ assert_accepts matcher, get_instance_of(klass)
414
347
  end
348
+ end
349
+ end
415
350
 
416
- should "not allow #{attribute} to be more than #{max}" do
417
- v = max + 1
418
- assert_bad_value(klass, attribute, v, high_message)
419
- end
351
+ # Deprecated. See should_validate_numericality_of
352
+ def should_only_allow_numeric_values_for(*attributes)
353
+ warn "[DEPRECATION] should_only_allow_numeric_values_for is " <<
354
+ "deprecated. Use should_validate_numericality_of instead."
355
+ should_validate_numericality_of(*attributes)
356
+ end
420
357
 
421
- should "allow #{attribute} to be #{max}" do
422
- v = max
423
- assert_good_value(klass, attribute, v, high_message)
358
+ # Ensures that the has_many relationship exists. Will also test that the
359
+ # associated table has the required columns. Works with polymorphic
360
+ # associations.
361
+ #
362
+ # Options:
363
+ # * <tt>:through</tt> - association name for <tt>has_many :through</tt>
364
+ # * <tt>:dependent</tt> - tests that the association makes use of the dependent option.
365
+ #
366
+ # Example:
367
+ # should_have_many :friends
368
+ # should_have_many :enemies, :through => :friends
369
+ # should_have_many :enemies, :dependent => :destroy
370
+ #
371
+ def should_have_many(*associations)
372
+ through, dependent = get_options!(associations, :through, :dependent)
373
+ klass = model_class
374
+ associations.each do |association|
375
+ matcher = have_many(association).through(through).dependent(dependent)
376
+ should matcher.description do
377
+ assert_accepts(matcher, klass.new)
424
378
  end
425
379
  end
380
+ end
426
381
 
427
- # Ensure that the attribute is numeric
428
- #
429
- # If an instance variable has been created in the setup named after the
430
- # model being tested, then this method will use that. Otherwise, it will
431
- # create a new instance to test against.
432
- #
433
- # Options:
434
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
435
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.not_a_number')</tt>
436
- #
437
- # Example:
438
- # should_only_allow_numeric_values_for :age
439
- #
440
- def should_only_allow_numeric_values_for(*attributes)
441
- message = get_options!(attributes, :message)
442
- message ||= default_error_message(:not_a_number)
443
- klass = model_class
444
- attributes.each do |attribute|
445
- attribute = attribute.to_sym
446
- should "only allow numeric values for #{attribute}" do
447
- assert_bad_value(klass, attribute, "abcd", message)
448
- end
382
+ # Ensure that the has_one relationship exists. Will also test that the
383
+ # associated table has the required columns. Works with polymorphic
384
+ # associations.
385
+ #
386
+ # Options:
387
+ # * <tt>:dependent</tt> - tests that the association makes use of the dependent option.
388
+ #
389
+ # Example:
390
+ # should_have_one :god # unless hindu
391
+ #
392
+ def should_have_one(*associations)
393
+ dependent = get_options!(associations, :dependent)
394
+ klass = model_class
395
+ associations.each do |association|
396
+ matcher = have_one(association).dependent(dependent)
397
+ should matcher.description do
398
+ assert_accepts(matcher, klass.new)
449
399
  end
450
400
  end
401
+ end
451
402
 
452
- # Ensures that the has_many relationship exists. Will also test that the
453
- # associated table has the required columns. Works with polymorphic
454
- # associations.
455
- #
456
- # Options:
457
- # * <tt>:through</tt> - association name for <tt>has_many :through</tt>
458
- # * <tt>:dependent</tt> - tests that the association makes use of the dependent option.
459
- #
460
- # Example:
461
- # should_have_many :friends
462
- # should_have_many :enemies, :through => :friends
463
- # should_have_many :enemies, :dependent => :destroy
464
- #
465
- def should_have_many(*associations)
466
- through, dependent = get_options!(associations, :through, :dependent)
467
- klass = model_class
468
- associations.each do |association|
469
- name = "have many #{association}"
470
- name += " through #{through}" if through
471
- name += " dependent => #{dependent}" if dependent
472
- should name do
473
- reflection = klass.reflect_on_association(association)
474
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
475
- assert_equal :has_many, reflection.macro
476
-
477
- if through
478
- through_reflection = klass.reflect_on_association(through)
479
- assert through_reflection, "#{klass.name} does not have any relationship to #{through}"
480
- assert_equal(through, reflection.options[:through])
481
- end
482
-
483
- if dependent
484
- assert_equal dependent.to_s,
485
- reflection.options[:dependent].to_s,
486
- "#{association} should have #{dependent} dependency"
487
- end
488
-
489
- # Check for the existence of the foreign key on the other table
490
- unless reflection.options[:through]
491
- if reflection.options[:foreign_key]
492
- fk = reflection.options[:foreign_key]
493
- elsif reflection.options[:as]
494
- fk = reflection.options[:as].to_s.foreign_key
495
- else
496
- fk = reflection.primary_key_name
497
- end
498
-
499
- associated_klass_name = (reflection.options[:class_name] || association.to_s.classify)
500
- associated_klass = associated_klass_name.constantize
501
-
502
- assert associated_klass.column_names.include?(fk.to_s),
503
- "#{associated_klass.name} does not have a #{fk} foreign key."
504
- end
505
- end
506
- end
507
- end
403
+ # Ensures that the has_and_belongs_to_many relationship exists, and that the join
404
+ # table is in place.
405
+ #
406
+ # should_have_and_belong_to_many :posts, :cars
407
+ #
408
+ def should_have_and_belong_to_many(*associations)
409
+ get_options!(associations)
410
+ klass = model_class
508
411
 
509
- # Ensure that the has_one relationship exists. Will also test that the
510
- # associated table has the required columns. Works with polymorphic
511
- # associations.
512
- #
513
- # Options:
514
- # * <tt>:dependent</tt> - tests that the association makes use of the dependent option.
515
- #
516
- # Example:
517
- # should_have_one :god # unless hindu
518
- #
519
- def should_have_one(*associations)
520
- dependent = get_options!(associations, :dependent)
521
- klass = model_class
522
- associations.each do |association|
523
- name = "have one #{association}"
524
- name += " dependent => #{dependent}" if dependent
525
- should name do
526
- reflection = klass.reflect_on_association(association)
527
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
528
- assert_equal :has_one, reflection.macro
529
-
530
- associated_klass = (reflection.options[:class_name] || association.to_s.camelize).constantize
531
-
532
- if reflection.options[:foreign_key]
533
- fk = reflection.options[:foreign_key]
534
- elsif reflection.options[:as]
535
- fk = reflection.options[:as].to_s.foreign_key
536
- fk_type = fk.gsub(/_id$/, '_type')
537
- assert associated_klass.column_names.include?(fk_type),
538
- "#{associated_klass.name} does not have a #{fk_type} column."
539
- else
540
- fk = klass.name.foreign_key
541
- end
542
- assert associated_klass.column_names.include?(fk.to_s),
543
- "#{associated_klass.name} does not have a #{fk} foreign key."
544
-
545
- if dependent
546
- assert_equal dependent.to_s,
547
- reflection.options[:dependent].to_s,
548
- "#{association} should have #{dependent} dependency"
549
- end
550
- end
412
+ associations.each do |association|
413
+ matcher = have_and_belong_to_many(association)
414
+ should matcher.description do
415
+ assert_accepts(matcher, klass.new)
551
416
  end
552
417
  end
418
+ end
553
419
 
554
- # Ensures that the has_and_belongs_to_many relationship exists, and that the join
555
- # table is in place.
556
- #
557
- # should_have_and_belong_to_many :posts, :cars
558
- #
559
- def should_have_and_belong_to_many(*associations)
560
- get_options!(associations)
561
- klass = model_class
562
-
563
- associations.each do |association|
564
- should "should have and belong to many #{association}" do
565
- reflection = klass.reflect_on_association(association)
566
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
567
- assert_equal :has_and_belongs_to_many, reflection.macro
568
- table = reflection.options[:join_table]
569
- assert ::ActiveRecord::Base.connection.tables.include?(table.to_s), "table #{table} doesn't exist"
570
- end
420
+ # Ensure that the belongs_to relationship exists.
421
+ #
422
+ # should_belong_to :parent
423
+ #
424
+ def should_belong_to(*associations)
425
+ dependent = get_options!(associations, :dependent)
426
+ klass = model_class
427
+ associations.each do |association|
428
+ matcher = belong_to(association).dependent(dependent)
429
+ should matcher.description do
430
+ assert_accepts(matcher, klass.new)
571
431
  end
572
432
  end
433
+ end
573
434
 
574
- # Ensure that the belongs_to relationship exists.
575
- #
576
- # should_belong_to :parent
577
- #
578
- def should_belong_to(*associations)
579
- get_options!(associations)
580
- klass = model_class
581
- associations.each do |association|
582
- should "belong_to #{association}" do
583
- reflection = klass.reflect_on_association(association)
584
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
585
- assert_equal :belongs_to, reflection.macro
586
-
587
- unless reflection.options[:polymorphic]
588
- associated_klass = (reflection.options[:class_name] || association.to_s.camelize).constantize
589
- fk = reflection.options[:foreign_key] || reflection.primary_key_name
590
- assert klass.column_names.include?(fk.to_s), "#{klass.name} does not have a #{fk} foreign key."
591
- end
592
- end
435
+ # Ensure that the given class methods are defined on the model.
436
+ #
437
+ # should_have_class_methods :find, :destroy
438
+ #
439
+ def should_have_class_methods(*methods)
440
+ get_options!(methods)
441
+ klass = model_class
442
+ methods.each do |method|
443
+ should "respond to class method ##{method}" do
444
+ assert_respond_to klass, method, "#{klass.name} does not have class method #{method}"
593
445
  end
594
446
  end
447
+ end
595
448
 
596
- # Ensure that the given class methods are defined on the model.
597
- #
598
- # should_have_class_methods :find, :destroy
599
- #
600
- def should_have_class_methods(*methods)
601
- get_options!(methods)
602
- klass = model_class
603
- methods.each do |method|
604
- should "respond to class method ##{method}" do
605
- assert_respond_to klass, method, "#{klass.name} does not have class method #{method}"
606
- end
449
+ # Ensure that the given instance methods are defined on the model.
450
+ #
451
+ # should_have_instance_methods :email, :name, :name=
452
+ #
453
+ def should_have_instance_methods(*methods)
454
+ get_options!(methods)
455
+ klass = model_class
456
+ methods.each do |method|
457
+ should "respond to instance method ##{method}" do
458
+ assert_respond_to klass.new, method, "#{klass.name} does not have instance method #{method}"
607
459
  end
608
460
  end
461
+ end
609
462
 
610
- # Ensure that the given instance methods are defined on the model.
611
- #
612
- # should_have_instance_methods :email, :name, :name=
613
- #
614
- def should_have_instance_methods(*methods)
615
- get_options!(methods)
616
- klass = model_class
617
- methods.each do |method|
618
- should "respond to instance method ##{method}" do
619
- assert_respond_to klass.new, method, "#{klass.name} does not have instance method #{method}"
620
- end
463
+ # Ensure that the given columns are defined on the models backing SQL table.
464
+ # Also aliased to should_have_index for readability.
465
+ # Takes the same options available in migrations:
466
+ # :type, :precision, :limit, :default, :null, and :scale
467
+ #
468
+ # Examples:
469
+ #
470
+ # should_have_db_columns :id, :email, :name, :created_at
471
+ #
472
+ # should_have_db_column :email, :type => "string", :limit => 255
473
+ # should_have_db_column :salary, :decimal, :precision => 15, :scale => 2
474
+ # should_have_db_column :admin, :default => false, :null => false
475
+ #
476
+ def should_have_db_columns(*columns)
477
+ column_type, precision, limit, default, null, scale, sql_type =
478
+ get_options!(columns, :type, :precision, :limit,
479
+ :default, :null, :scale, :sql_type)
480
+ klass = model_class
481
+ columns.each do |name|
482
+ matcher = have_db_column(name).
483
+ of_type(column_type).
484
+ with_options(:precision => precision, :limit => limit,
485
+ :default => default, :null => null,
486
+ :scale => scale, :sql_type => sql_type)
487
+ should matcher.description do
488
+ assert_accepts(matcher, klass.new)
621
489
  end
622
490
  end
491
+ end
492
+
493
+ alias_method :should_have_db_column, :should_have_db_columns
623
494
 
624
- # Ensure that the given columns are defined on the models backing SQL table.
625
- #
626
- # should_have_db_columns :id, :email, :name, :created_at
627
- #
628
- def should_have_db_columns(*columns)
629
- column_type = get_options!(columns, :type)
630
- klass = model_class
631
- columns.each do |name|
632
- test_name = "have column #{name}"
633
- test_name += " of type #{column_type}" if column_type
634
- should test_name do
635
- column = klass.columns.detect {|c| c.name == name.to_s }
636
- assert column, "#{klass.name} does not have column #{name}"
637
- end
495
+ # Ensures that there are DB indices on the given columns or tuples of columns.
496
+ # Also aliased to should_have_index for readability
497
+ #
498
+ # Options:
499
+ # * <tt>:unique</tt> - whether or not the index has a unique
500
+ # constraint. Use <tt>true</tt> to explicitly test for a unique
501
+ # constraint. Use <tt>false</tt> to explicitly test for a non-unique
502
+ # constraint. Use <tt>nil</tt> if you don't care whether the index is
503
+ # unique or not. Default = <tt>nil</tt>
504
+ #
505
+ # Examples:
506
+ #
507
+ # should_have_indices :email, :name, [:commentable_type, :commentable_id]
508
+ # should_have_index :age
509
+ # should_have_index :ssn, :unique => true
510
+ #
511
+ def should_have_indices(*columns)
512
+ unique = get_options!(columns, :unique)
513
+ klass = model_class
514
+
515
+ columns.each do |column|
516
+ matcher = have_index(column).unique(unique)
517
+ should matcher.description do
518
+ assert_accepts(matcher, klass.new)
638
519
  end
639
520
  end
521
+ end
640
522
 
641
- # Ensure that the given column is defined on the models backing SQL table. The options are the same as
642
- # the instance variables defined on the column definition: :precision, :limit, :default, :null,
643
- # :primary, :type, :scale, and :sql_type.
644
- #
645
- # should_have_db_column :email, :type => "string", :default => nil, :precision => nil, :limit => 255,
646
- # :null => true, :primary => false, :scale => nil, :sql_type => 'varchar(255)'
647
- #
648
- def should_have_db_column(name, opts = {})
649
- klass = model_class
650
- test_name = "have column named :#{name}"
651
- test_name += " with options " + opts.inspect unless opts.empty?
652
- should test_name do
653
- column = klass.columns.detect {|c| c.name == name.to_s }
654
- assert column, "#{klass.name} does not have column #{name}"
655
- opts.each do |k, v|
656
- assert_equal column.instance_variable_get("@#{k}").to_s, v.to_s, ":#{name} column on table for #{klass} does not match option :#{k}"
657
- end
658
- end
659
- end
523
+ alias_method :should_have_index, :should_have_indices
660
524
 
661
- # Ensures that there are DB indices on the given columns or tuples of columns.
662
- # Also aliased to should_have_index for readability
663
- #
664
- # should_have_indices :email, :name, [:commentable_type, :commentable_id]
665
- # should_have_index :age
666
- #
667
- def should_have_indices(*columns)
668
- table = model_class.table_name
669
- indices = ::ActiveRecord::Base.connection.indexes(table).map(&:columns)
670
-
671
- columns.each do |column|
672
- should "have index on #{table} for #{column.inspect}" do
673
- columns = [column].flatten.map(&:to_s)
674
- assert_contains(indices, columns)
675
- end
676
- end
677
- end
525
+ # Ensures that the model cannot be saved if one of the attributes listed is not accepted.
526
+ #
527
+ # If an instance variable has been created in the setup named after the
528
+ # model being tested, then this method will use that. Otherwise, it will
529
+ # create a new instance to test against.
530
+ #
531
+ # Options:
532
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
533
+ # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.accepted')</tt>
534
+ #
535
+ # Example:
536
+ # should_validate_acceptance_of :eula
537
+ #
538
+ def should_validate_acceptance_of(*attributes)
539
+ message = get_options!(attributes, :message)
540
+ klass = model_class
678
541
 
679
- alias_method :should_have_index, :should_have_indices
680
-
681
- # Ensures that the model cannot be saved if one of the attributes listed is not accepted.
682
- #
683
- # If an instance variable has been created in the setup named after the
684
- # model being tested, then this method will use that. Otherwise, it will
685
- # create a new instance to test against.
686
- #
687
- # Options:
688
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
689
- # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.accepted')</tt>
690
- #
691
- # Example:
692
- # should_require_acceptance_of :eula
693
- #
694
- def should_require_acceptance_of(*attributes)
695
- message = get_options!(attributes, :message)
696
- message ||= default_error_message(:accepted)
697
- klass = model_class
698
-
699
- attributes.each do |attribute|
700
- should "require #{attribute} to be accepted" do
701
- assert_bad_value(klass, attribute, false, message)
702
- end
542
+ attributes.each do |attribute|
543
+ matcher = validate_acceptance_of(attribute).with_message(message)
544
+ should matcher.description do
545
+ assert_accepts matcher, get_instance_of(klass)
703
546
  end
704
547
  end
548
+ end
705
549
 
706
- # Ensures that the model has a method named scope_name that returns a NamedScope object with the
707
- # proxy options set to the options you supply. scope_name can be either a symbol, or a method
708
- # call which will be evaled against the model. The eval'd method call has access to all the same
709
- # instance variables that a should statement would.
710
- #
711
- # Options: Any of the options that the named scope would pass on to find.
712
- #
713
- # Example:
714
- #
715
- # should_have_named_scope :visible, :conditions => {:visible => true}
716
- #
717
- # Passes for
718
- #
719
- # named_scope :visible, :conditions => {:visible => true}
720
- #
721
- # Or for
722
- #
723
- # def self.visible
724
- # scoped(:conditions => {:visible => true})
725
- # end
726
- #
727
- # You can test lambdas or methods that return ActiveRecord#scoped calls:
728
- #
729
- # should_have_named_scope 'recent(5)', :limit => 5
730
- # should_have_named_scope 'recent(1)', :limit => 1
731
- #
732
- # Passes for
733
- # named_scope :recent, lambda {|c| {:limit => c}}
734
- #
735
- # Or for
736
- #
737
- # def self.recent(c)
738
- # scoped(:limit => c)
739
- # end
740
- #
741
- def should_have_named_scope(scope_call, *args)
742
- klass = model_class
743
- scope_opts = args.extract_options!
744
- scope_call = scope_call.to_s
745
-
746
- context scope_call do
747
- setup do
748
- @scope = eval("#{klass}.#{scope_call}")
749
- end
750
-
751
- should "return a scope object" do
752
- assert_equal ::ActiveRecord::NamedScope::Scope, @scope.class
753
- end
754
-
755
- unless scope_opts.empty?
756
- should "scope itself to #{scope_opts.inspect}" do
757
- assert_equal scope_opts, @scope.proxy_options
758
- end
759
- end
760
- end
550
+ # Deprecated. See should_validate_uniqueness_of
551
+ def should_require_acceptance_of(*attributes)
552
+ warn "[DEPRECATION] should_require_acceptance_of is deprecated. " <<
553
+ "Use should_validate_acceptance_of instead."
554
+ should_validate_acceptance_of(*attributes)
555
+ end
556
+
557
+ # Ensures that the model has a method named scope_name that returns a NamedScope object with the
558
+ # proxy options set to the options you supply. scope_name can be either a symbol, or a method
559
+ # call which will be evaled against the model. The eval'd method call has access to all the same
560
+ # instance variables that a should statement would.
561
+ #
562
+ # Options: Any of the options that the named scope would pass on to find.
563
+ #
564
+ # Example:
565
+ #
566
+ # should_have_named_scope :visible, :conditions => {:visible => true}
567
+ #
568
+ # Passes for
569
+ #
570
+ # named_scope :visible, :conditions => {:visible => true}
571
+ #
572
+ # Or for
573
+ #
574
+ # def self.visible
575
+ # scoped(:conditions => {:visible => true})
576
+ # end
577
+ #
578
+ # You can test lambdas or methods that return ActiveRecord#scoped calls:
579
+ #
580
+ # should_have_named_scope 'recent(5)', :limit => 5
581
+ # should_have_named_scope 'recent(1)', :limit => 1
582
+ #
583
+ # Passes for
584
+ # named_scope :recent, lambda {|c| {:limit => c}}
585
+ #
586
+ # Or for
587
+ #
588
+ # def self.recent(c)
589
+ # scoped(:limit => c)
590
+ # end
591
+ #
592
+ def should_have_named_scope(scope_call, find_options = nil)
593
+ klass = model_class
594
+ matcher = have_named_scope(scope_call).finding(find_options)
595
+ should matcher.description do
596
+ assert_accepts matcher.in_context(self), klass.new
761
597
  end
762
598
  end
763
599
  end