shoulda-matchers 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -3
  4. data/CONTRIBUTING.md +60 -28
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +15 -12
  7. data/NEWS.md +111 -0
  8. data/README.md +94 -6
  9. data/Rakefile +10 -8
  10. data/custom_plan.rb +88 -0
  11. data/gemfiles/4.0.0.gemfile +1 -0
  12. data/gemfiles/4.0.0.gemfile.lock +21 -18
  13. data/gemfiles/4.0.1.gemfile +1 -0
  14. data/gemfiles/4.0.1.gemfile.lock +21 -18
  15. data/gemfiles/4.1.gemfile +1 -0
  16. data/gemfiles/4.1.gemfile.lock +21 -18
  17. data/gemfiles/4.2.gemfile +1 -0
  18. data/gemfiles/4.2.gemfile.lock +24 -21
  19. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -11
  20. data/lib/shoulda/matchers/active_model.rb +10 -1
  21. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +258 -180
  22. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
  23. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
  24. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +236 -0
  25. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
  26. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
  27. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
  28. data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
  29. data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
  30. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +34 -14
  31. data/lib/shoulda/matchers/active_model/helpers.rb +9 -17
  32. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +13 -6
  33. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +13 -2
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +19 -35
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +13 -2
  36. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +12 -2
  37. data/lib/shoulda/matchers/active_model/qualifiers.rb +12 -0
  38. data/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
  39. data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
  40. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -32
  41. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -8
  42. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -22
  43. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +27 -16
  44. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +58 -15
  45. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +22 -12
  46. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +165 -87
  47. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +7 -9
  48. data/lib/shoulda/matchers/active_model/validation_matcher.rb +111 -49
  49. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +60 -0
  50. data/lib/shoulda/matchers/active_model/validator.rb +71 -52
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +19 -5
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +450 -124
  53. data/lib/shoulda/matchers/util.rb +43 -0
  54. data/lib/shoulda/matchers/util/word_wrap.rb +59 -31
  55. data/lib/shoulda/matchers/version.rb +1 -1
  56. data/script/update_gem_in_all_appraisals +1 -1
  57. data/script/update_gems_in_all_appraisals +1 -1
  58. data/spec/acceptance/multiple_libraries_integration_spec.rb +5 -2
  59. data/spec/acceptance/rails_integration_spec.rb +6 -2
  60. data/spec/spec_helper.rb +1 -3
  61. data/spec/support/acceptance/helpers/step_helpers.rb +4 -1
  62. data/spec/support/tests/current_bundle.rb +21 -7
  63. data/spec/support/unit/active_record/create_table.rb +54 -0
  64. data/spec/support/unit/attribute.rb +47 -0
  65. data/spec/support/unit/capture.rb +6 -0
  66. data/spec/support/unit/change_value.rb +111 -0
  67. data/spec/support/unit/create_model_arguments/basic.rb +135 -0
  68. data/spec/support/unit/create_model_arguments/has_many.rb +15 -0
  69. data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +74 -0
  70. data/spec/support/unit/helpers/active_record_versions.rb +1 -1
  71. data/spec/support/unit/helpers/class_builder.rb +61 -47
  72. data/spec/support/unit/helpers/database_helpers.rb +5 -3
  73. data/spec/support/unit/helpers/model_builder.rb +77 -97
  74. data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +44 -0
  75. data/spec/support/unit/load_environment.rb +12 -0
  76. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +2 -2
  77. data/spec/support/unit/matchers/fail_with_message_matcher.rb +12 -1
  78. data/spec/support/unit/model_creation_strategies/active_model.rb +111 -0
  79. data/spec/support/unit/model_creation_strategies/active_record.rb +77 -0
  80. data/spec/support/unit/model_creators.rb +19 -0
  81. data/spec/support/unit/model_creators/active_model.rb +39 -0
  82. data/spec/support/unit/model_creators/active_record.rb +43 -0
  83. data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +95 -0
  84. data/spec/support/unit/model_creators/active_record/has_many.rb +67 -0
  85. data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +42 -0
  86. data/spec/support/unit/model_creators/basic.rb +97 -0
  87. data/spec/support/unit/rails_application.rb +1 -1
  88. data/spec/support/unit/record_validating_confirmation_builder.rb +3 -7
  89. data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +79 -0
  90. data/spec/support/unit/validation_matcher_scenario.rb +62 -0
  91. data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +4 -0
  92. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +575 -140
  93. data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +115 -15
  94. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +42 -4
  95. data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +92 -6
  96. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +122 -10
  97. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +306 -58
  98. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +122 -3
  99. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +805 -131
  100. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +196 -29
  101. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +82 -40
  102. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +600 -101
  103. data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +88 -33
  104. data/spec/unit_spec_helper.rb +10 -22
  105. data/zeus.json +11 -0
  106. metadata +64 -23
  107. data/lib/shoulda/matchers/active_model/strict_validator.rb +0 -51
  108. data/spec/support/unit/shared_examples/numerical_type_submatcher.rb +0 -15
  109. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +0 -288
  110. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +0 -100
  111. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +0 -100
  112. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +0 -100
@@ -21,6 +21,41 @@ describe Shoulda::Matchers::ActiveModel::ValidateLengthOfMatcher, type: :model d
21
21
  expect(validating_length(minimum: 4)).
22
22
  to validate_length_of(:attr).is_at_least(4).with_short_message(nil)
23
23
  end
24
+
25
+ it_supports(
26
+ 'ignoring_interference_by_writer',
27
+ tests: {
28
+ accept_if_qualified_but_changing_value_does_not_interfere: {
29
+ changing_values_with: :upcase,
30
+ },
31
+ reject_if_qualified_but_changing_value_interferes: {
32
+ model_name: 'Example',
33
+ attribute_name: :attr,
34
+ changing_values_with: :add_character,
35
+ expected_message: <<-MESSAGE.strip
36
+ Example did not properly validate that the length of :attr is at least
37
+ 4.
38
+ After setting :attr to ‹"xxx"› -- which was read back as ‹"xxxa"› --
39
+ the matcher expected the Example to be invalid, but it was valid
40
+ instead.
41
+
42
+ As indicated in the message above, :attr seems to be changing certain
43
+ values as they are set, and this could have something to do with why
44
+ this test is failing. If you've overridden the writer method for this
45
+ attribute, then you may need to change it to make this test pass, or
46
+ do something else entirely.
47
+ MESSAGE
48
+ }
49
+ }
50
+ ) do
51
+ def validation_matcher_scenario_args
52
+ super.deep_merge(validation_options: { minimum: 4 })
53
+ end
54
+
55
+ def configure_validation_matcher(matcher)
56
+ matcher.is_at_least(4)
57
+ end
58
+ end
24
59
  end
25
60
 
26
61
  context 'an attribute with a minimum length validation of 0' do
@@ -50,6 +85,40 @@ describe Shoulda::Matchers::ActiveModel::ValidateLengthOfMatcher, type: :model d
50
85
  expect(validating_length(maximum: 4)).
51
86
  to validate_length_of(:attr).is_at_most(4).with_long_message(nil)
52
87
  end
88
+
89
+ it_supports(
90
+ 'ignoring_interference_by_writer',
91
+ tests: {
92
+ accept_if_qualified_but_changing_value_does_not_interfere: {
93
+ changing_values_with: :upcase,
94
+ },
95
+ reject_if_qualified_but_changing_value_interferes: {
96
+ model_name: 'Example',
97
+ attribute_name: :attr,
98
+ changing_values_with: :remove_character,
99
+ expected_message: <<-MESSAGE.strip
100
+ Example did not properly validate that the length of :attr is at most 4.
101
+ After setting :attr to ‹"xxxxx"› -- which was read back as ‹"xxxx"› --
102
+ the matcher expected the Example to be invalid, but it was valid
103
+ instead.
104
+
105
+ As indicated in the message above, :attr seems to be changing certain
106
+ values as they are set, and this could have something to do with why
107
+ this test is failing. If you've overridden the writer method for this
108
+ attribute, then you may need to change it to make this test pass, or
109
+ do something else entirely.
110
+ MESSAGE
111
+ }
112
+ }
113
+ ) do
114
+ def validation_matcher_scenario_args
115
+ super.deep_merge(validation_options: { maximum: 4 })
116
+ end
117
+
118
+ def configure_validation_matcher(matcher)
119
+ matcher.is_at_most(4)
120
+ end
121
+ end
53
122
  end
54
123
 
55
124
  context 'an attribute with a required exact length' do
@@ -72,6 +141,40 @@ describe Shoulda::Matchers::ActiveModel::ValidateLengthOfMatcher, type: :model d
72
141
  expect(validating_length(is: 4)).
73
142
  to validate_length_of(:attr).is_equal_to(4).with_message(nil)
74
143
  end
144
+
145
+ it_supports(
146
+ 'ignoring_interference_by_writer',
147
+ tests: {
148
+ accept_if_qualified_but_changing_value_does_not_interfere: {
149
+ changing_values_with: :upcase,
150
+ },
151
+ reject_if_qualified_but_changing_value_interferes: {
152
+ model_name: 'Example',
153
+ attribute_name: :attr,
154
+ changing_values_with: :add_character,
155
+ expected_message: <<-MESSAGE.strip
156
+ Example did not properly validate that the length of :attr is 4.
157
+ After setting :attr to ‹"xxx"› -- which was read back as ‹"xxxa"› --
158
+ the matcher expected the Example to be invalid, but it was valid
159
+ instead.
160
+
161
+ As indicated in the message above, :attr seems to be changing certain
162
+ values as they are set, and this could have something to do with why
163
+ this test is failing. If you've overridden the writer method for this
164
+ attribute, then you may need to change it to make this test pass, or
165
+ do something else entirely.
166
+ MESSAGE
167
+ }
168
+ }
169
+ ) do
170
+ def validation_matcher_scenario_args
171
+ super.deep_merge(validation_options: { is: 4 })
172
+ end
173
+
174
+ def configure_validation_matcher(matcher)
175
+ matcher.is_equal_to(4)
176
+ end
177
+ end
75
178
  end
76
179
 
77
180
  context 'an attribute with a required exact length and another validation' do
@@ -161,9 +264,25 @@ describe Shoulda::Matchers::ActiveModel::ValidateLengthOfMatcher, type: :model d
161
264
  end
162
265
  end
163
266
 
267
+ def define_model_validating_length(options = {})
268
+ options = options.dup
269
+ attribute_name = options.delete(:attribute_name) { :attr }
270
+
271
+ define_model(:example, attribute_name => :string) do |model|
272
+ model.validates_length_of(attribute_name, options)
273
+ end
274
+ end
275
+
164
276
  def validating_length(options = {})
165
- define_model(:example, attr: :string) do
166
- validates_length_of :attr, options
167
- end.new
277
+ define_model_validating_length(options).new
278
+ end
279
+
280
+ alias_method :build_record_validating_length, :validating_length
281
+
282
+ def validation_matcher_scenario_args
283
+ super.deep_merge(
284
+ matcher_name: :validate_length_of,
285
+ model_creator: :active_model
286
+ )
168
287
  end
169
288
  end
@@ -127,39 +127,65 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
127
127
  expect(record).to validate_numericality
128
128
  end
129
129
 
130
- context 'when the column is an integer column' do
131
- it 'raises an IneffectiveTestError' do
132
- record = build_record_validating_numericality(
133
- column_type: :integer
134
- )
135
- assertion = -> { expect(record).to validate_numericality }
130
+ it_supports(
131
+ 'ignoring_interference_by_writer',
132
+ tests: {
133
+ accept_if_qualified_but_changing_value_does_not_interfere: {
134
+ changing_values_with: :next_value,
135
+ },
136
+ reject_if_qualified_but_changing_value_interferes: {
137
+ model_name: 'Example',
138
+ attribute_name: :attr,
139
+ changing_values_with: :numeric_value,
140
+ expected_message: <<-MESSAGE.strip
141
+ Example did not properly validate that :attr looks like a number.
142
+ After setting :attr to ‹"abcd"› -- which was read back as ‹"1"› -- the
143
+ matcher expected the Example to be invalid, but it was valid instead.
144
+
145
+ As indicated in the message above, :attr seems to be changing certain
146
+ values as they are set, and this could have something to do with why
147
+ this test is failing. If you've overridden the writer method for this
148
+ attribute, then you may need to change it to make this test pass, or
149
+ do something else entirely.
150
+ MESSAGE
151
+ }
152
+ }
153
+ )
136
154
 
137
- expect(&assertion).
138
- to raise_error(described_class::IneffectiveTestError)
155
+ context 'when the attribute is a virtual attribute in an ActiveRecord model' do
156
+ it 'accepts' do
157
+ record = build_record_validating_numericality_of_virtual_attribute
158
+ expect(record).to validate_numericality
139
159
  end
140
160
  end
141
161
 
142
- context 'when the column is a float column' do
143
- it 'raises an IneffectiveTestError' do
144
- record = build_record_validating_numericality(
145
- column_type: :float
146
- )
147
- assertion = -> { expect(record).to validate_numericality }
162
+ context 'when the column is an integer column' do
163
+ it 'accepts (and does not raise an AttributeChangedValueError)' do
164
+ record = build_record_validating_numericality(column_type: :integer)
165
+ expect(record).to validate_numericality
166
+ end
167
+ end
148
168
 
149
- expect(&assertion).
150
- to raise_error(described_class::IneffectiveTestError)
169
+ context 'when the column is a float column' do
170
+ it 'accepts (and does not raise an AttributeChangedValueError)' do
171
+ record = build_record_validating_numericality(column_type: :float)
172
+ expect(record).to validate_numericality
151
173
  end
152
174
  end
153
175
 
154
176
  context 'when the column is a decimal column' do
155
- it 'raises an IneffectiveTestError' do
156
- record = build_record_validating_numericality(
157
- column_type: :decimal,
158
- )
159
- assertion = -> { expect(record).to validate_numericality }
177
+ it 'accepts (and does not raise an AttributeChangedValueError)' do
178
+ record = build_record_validating_numericality(column_type: :decimal)
179
+ expect(record).to validate_numericality
180
+ end
181
+ end
160
182
 
161
- expect(&assertion).
162
- to raise_error(described_class::IneffectiveTestError)
183
+ if database_supports_money_columns?
184
+ context 'when the column is a money column' do
185
+ it 'accepts (and does not raise an AttributeChangedValueError)' do
186
+ record = build_record_validating_numericality(column_type: :money)
187
+ expect(record).to validate_numericality
188
+ end
163
189
  end
164
190
  end
165
191
  end
@@ -167,10 +193,16 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
167
193
  context 'and not validating anything' do
168
194
  it 'rejects since it does not disallow non-numbers' do
169
195
  record = build_record_validating_nothing
196
+
170
197
  assertion = -> { expect(record).to validate_numericality }
171
- expect(&assertion).to fail_with_message_including(
172
- 'Expected errors to include "is not a number"'
173
- )
198
+
199
+ message = <<-MESSAGE
200
+ Example did not properly validate that :attr looks like a number.
201
+ After setting :attr to ‹"abcd"›, the matcher expected the Example to
202
+ be invalid, but it was valid instead.
203
+ MESSAGE
204
+
205
+ expect(&assertion).to fail_with_message(message)
174
206
  end
175
207
  end
176
208
  end
@@ -181,17 +213,65 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
181
213
  record = build_record_validating_numericality(allow_nil: true)
182
214
  expect(record).to validate_numericality.allow_nil
183
215
  end
216
+
217
+ it_supports(
218
+ 'ignoring_interference_by_writer',
219
+ tests: {
220
+ accept_if_qualified_but_changing_value_does_not_interfere: {
221
+ changing_values_with: :next_value_or_numeric_value,
222
+ },
223
+ reject_if_qualified_but_changing_value_interferes: {
224
+ model_name: 'Example',
225
+ attribute_name: :attr,
226
+ changing_values_with: :next_value_or_non_numeric_value,
227
+ expected_message: <<-MESSAGE.strip
228
+ Example did not properly validate that :attr looks like a number, but
229
+ only if it is not nil.
230
+ In checking that Example allows :attr to be ‹nil›, after setting :attr
231
+ to ‹nil› -- which was read back as ‹"a"› -- the matcher expected the
232
+ Example to be valid, but it was invalid instead, producing these
233
+ validation errors:
234
+
235
+ * attr: ["is not a number"]
236
+
237
+ As indicated in the message above, :attr seems to be changing certain
238
+ values as they are set, and this could have something to do with why
239
+ this test is failing. If you've overridden the writer method for this
240
+ attribute, then you may need to change it to make this test pass, or
241
+ do something else entirely.
242
+ MESSAGE
243
+ }
244
+ }
245
+ ) do
246
+ def validation_matcher_scenario_args
247
+ super.deep_merge(validation_options: { allow_nil: true })
248
+ end
249
+
250
+ def configure_validation_matcher(matcher)
251
+ matcher.allow_nil
252
+ end
253
+ end
184
254
  end
185
255
 
186
256
  context 'and not validating with allow_nil' do
187
257
  it 'rejects since it tries to treat nil as a number' do
188
258
  record = build_record_validating_numericality
259
+
189
260
  assertion = lambda do
190
261
  expect(record).to validate_numericality.allow_nil
191
262
  end
192
- expect(&assertion).to fail_with_message_including(
193
- %[Did not expect errors to include "is not a number" when #{attribute_name} is set to nil]
194
- )
263
+
264
+ message = <<-MESSAGE
265
+ Example did not properly validate that :attr looks like a number, but
266
+ only if it is not nil.
267
+ In checking that Example allows :attr to be ‹nil›, after setting :attr
268
+ to ‹nil›, the matcher expected the Example to be valid, but it was
269
+ invalid instead, producing these validation errors:
270
+
271
+ * attr: ["is not a number"]
272
+ MESSAGE
273
+
274
+ expect(&assertion).to fail_with_message(message)
195
275
  end
196
276
  end
197
277
  end
@@ -202,17 +282,56 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
202
282
  record = build_record_validating_numericality(only_integer: true)
203
283
  expect(record).to validate_numericality.only_integer
204
284
  end
285
+
286
+ it_supports(
287
+ 'ignoring_interference_by_writer',
288
+ tests: {
289
+ accept_if_qualified_but_changing_value_does_not_interfere: {
290
+ changing_values_with: :next_value,
291
+ },
292
+ reject_if_qualified_but_changing_value_interferes: {
293
+ model_name: 'Example',
294
+ attribute_name: :attr,
295
+ changing_values_with: :numeric_value,
296
+ expected_message: <<-MESSAGE.strip
297
+ Example did not properly validate that :attr looks like an integer.
298
+ After setting :attr to ‹"0.1"› -- which was read back as ‹"1"› -- the
299
+ matcher expected the Example to be invalid, but it was valid instead.
300
+
301
+ As indicated in the message above, :attr seems to be changing certain
302
+ values as they are set, and this could have something to do with why
303
+ this test is failing. If you've overridden the writer method for this
304
+ attribute, then you may need to change it to make this test pass, or
305
+ do something else entirely.
306
+ MESSAGE
307
+ }
308
+ }
309
+ ) do
310
+ def validation_matcher_scenario_args
311
+ super.deep_merge(validation_options: { only_integer: true })
312
+ end
313
+
314
+ def configure_validation_matcher(matcher)
315
+ matcher.only_integer
316
+ end
317
+ end
205
318
  end
206
319
 
207
320
  context 'and not validating with only_integer' do
208
321
  it 'rejects since it does not disallow non-integers' do
209
322
  record = build_record_validating_numericality
323
+
210
324
  assertion = lambda do
211
325
  expect(record).to validate_numericality.only_integer
212
326
  end
213
- expect(&assertion).to fail_with_message_including(
214
- 'Expected errors to include "must be an integer"'
215
- )
327
+
328
+ message = <<-MESSAGE
329
+ Example did not properly validate that :attr looks like an integer.
330
+ After setting :attr to ‹"0.1"›, the matcher expected the Example to be
331
+ invalid, but it was valid instead.
332
+ MESSAGE
333
+
334
+ expect(&assertion).to fail_with_message(message)
216
335
  end
217
336
  end
218
337
  end
@@ -224,6 +343,48 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
224
343
  expect(record).to validate_numericality.odd
225
344
  end
226
345
 
346
+ it_supports(
347
+ 'ignoring_interference_by_writer',
348
+ tests: {
349
+ accept_if_qualified_but_changing_value_does_not_interfere: {
350
+ changing_values_with: :next_next_value,
351
+ },
352
+ reject_if_qualified_but_changing_value_interferes: {
353
+ model_name: 'Example',
354
+ attribute_name: :attr,
355
+ changing_values_with: :next_value,
356
+ expected_message: <<-MESSAGE.strip
357
+ Example did not properly validate that :attr looks like an odd number.
358
+ After setting :attr to ‹"2"› -- which was read back as ‹"3"› -- the
359
+ matcher expected the Example to be invalid, but it was valid instead.
360
+
361
+ As indicated in the message above, :attr seems to be changing certain
362
+ values as they are set, and this could have something to do with why
363
+ this test is failing. If you've overridden the writer method for this
364
+ attribute, then you may need to change it to make this test pass, or
365
+ do something else entirely.
366
+ MESSAGE
367
+ }
368
+ }
369
+ ) do
370
+ def validation_matcher_scenario_args
371
+ super.deep_merge(validation_options: { odd: true })
372
+ end
373
+
374
+ def configure_validation_matcher(matcher)
375
+ matcher.odd
376
+ end
377
+ end
378
+
379
+ context 'when the attribute is a virtual attribute in ActiveRecord model' do
380
+ it 'accepts' do
381
+ record = build_record_validating_numericality_of_virtual_attribute(
382
+ odd: true
383
+ )
384
+ expect(record).to validate_numericality.odd
385
+ end
386
+ end
387
+
227
388
  context 'when the column is an integer column' do
228
389
  it 'accepts (and does not raise an error)' do
229
390
  record = build_record_validating_numericality(
@@ -261,12 +422,18 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
261
422
  context 'and not validating with odd' do
262
423
  it 'rejects since it does not disallow even numbers' do
263
424
  record = build_record_validating_numericality
425
+
264
426
  assertion = lambda do
265
427
  expect(record).to validate_numericality.odd
266
428
  end
267
- expect(&assertion).to fail_with_message_including(
268
- 'Expected errors to include "must be odd"'
269
- )
429
+
430
+ message = <<-MESSAGE
431
+ Example did not properly validate that :attr looks like an odd number.
432
+ After setting :attr to ‹"2"›, the matcher expected the Example to be
433
+ invalid, but it was valid instead.
434
+ MESSAGE
435
+
436
+ expect(&assertion).to fail_with_message(message)
270
437
  end
271
438
  end
272
439
  end
@@ -278,6 +445,48 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
278
445
  expect(record).to validate_numericality.even
279
446
  end
280
447
 
448
+ it_supports(
449
+ 'ignoring_interference_by_writer',
450
+ tests: {
451
+ accept_if_qualified_but_changing_value_does_not_interfere: {
452
+ changing_values_with: :next_next_value,
453
+ },
454
+ reject_if_qualified_but_changing_value_interferes: {
455
+ model_name: 'Example',
456
+ attribute_name: :attr,
457
+ changing_values_with: :next_value,
458
+ expected_message: <<-MESSAGE.strip
459
+ Example did not properly validate that :attr looks like an even number.
460
+ After setting :attr to ‹"1"› -- which was read back as ‹"2"› -- the
461
+ matcher expected the Example to be invalid, but it was valid instead.
462
+
463
+ As indicated in the message above, :attr seems to be changing certain
464
+ values as they are set, and this could have something to do with why
465
+ this test is failing. If you've overridden the writer method for this
466
+ attribute, then you may need to change it to make this test pass, or
467
+ do something else entirely.
468
+ MESSAGE
469
+ }
470
+ }
471
+ ) do
472
+ def validation_matcher_scenario_args
473
+ super.deep_merge(validation_options: { even: true })
474
+ end
475
+
476
+ def configure_validation_matcher(matcher)
477
+ matcher.even
478
+ end
479
+ end
480
+
481
+ context 'when the attribute is a virtual attribute in an ActiveRecord model' do
482
+ it 'accepts' do
483
+ record = build_record_validating_numericality_of_virtual_attribute(
484
+ even: true,
485
+ )
486
+ expect(record).to validate_numericality.even
487
+ end
488
+ end
489
+
281
490
  context 'when the column is an integer column' do
282
491
  it 'accepts (and does not raise an error)' do
283
492
  record = build_record_validating_numericality(
@@ -315,10 +524,18 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
315
524
  context 'and not validating with even' do
316
525
  it 'rejects since it does not disallow odd numbers' do
317
526
  record = build_record_validating_numericality
318
- assertion = -> { expect(record).to validate_numericality.even }
319
- expect(&assertion).to fail_with_message_including(
320
- 'Expected errors to include "must be even"'
321
- )
527
+
528
+ assertion = lambda do
529
+ expect(record).to validate_numericality.even
530
+ end
531
+
532
+ message = <<-MESSAGE
533
+ Example did not properly validate that :attr looks like an even number.
534
+ After setting :attr to ‹"1"›, the matcher expected the Example to be
535
+ invalid, but it was valid instead.
536
+ MESSAGE
537
+
538
+ expect(&assertion).to fail_with_message(message)
322
539
  end
323
540
  end
324
541
  end
@@ -332,6 +549,51 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
332
549
  expect(record).to validate_numericality.is_less_than_or_equal_to(18)
333
550
  end
334
551
 
552
+ it_supports(
553
+ 'ignoring_interference_by_writer',
554
+ tests: {
555
+ reject_if_qualified_but_changing_value_interferes: {
556
+ model_name: 'Example',
557
+ attribute_name: :attr,
558
+ changing_values_with: :next_value,
559
+ expected_message: <<-MESSAGE.strip
560
+ Example did not properly validate that :attr looks like a number less
561
+ than or equal to 18.
562
+ After setting :attr to ‹"18"› -- which was read back as ‹"19"› -- the
563
+ matcher expected the Example to be valid, but it was invalid instead,
564
+ producing these validation errors:
565
+
566
+ * attr: ["must be less than or equal to 18"]
567
+
568
+ As indicated in the message above, :attr seems to be changing certain
569
+ values as they are set, and this could have something to do with why
570
+ this test is failing. If you've overridden the writer method for this
571
+ attribute, then you may need to change it to make this test pass, or
572
+ do something else entirely.
573
+ MESSAGE
574
+ }
575
+ }
576
+ ) do
577
+ def validation_matcher_scenario_args
578
+ super.deep_merge(
579
+ validation_options: { less_than_or_equal_to: 18 }
580
+ )
581
+ end
582
+
583
+ def configure_validation_matcher(matcher)
584
+ matcher.is_less_than_or_equal_to(18)
585
+ end
586
+ end
587
+
588
+ context 'when the attribute is a virtual attribute in an ActiveRecord model' do
589
+ it 'accepts' do
590
+ record = build_record_validating_numericality_of_virtual_attribute(
591
+ less_than_or_equal_to: 18,
592
+ )
593
+ expect(record).to validate_numericality.is_less_than_or_equal_to(18)
594
+ end
595
+ end
596
+
335
597
  context 'when the column is an integer column' do
336
598
  it 'accepts (and does not raise an error)' do
337
599
  record = build_record_validating_numericality(
@@ -369,14 +631,19 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
369
631
  context 'and not validating with less_than_or_equal_to' do
370
632
  it 'rejects since it does not disallow numbers greater than the value' do
371
633
  record = build_record_validating_numericality
634
+
372
635
  assertion = lambda do
373
- expect(record).
374
- to validate_numericality.
375
- is_less_than_or_equal_to(18)
636
+ expect(record).to validate_numericality.is_less_than_or_equal_to(18)
376
637
  end
377
- expect(&assertion).to fail_with_message_including(
378
- 'Expected errors to include "must be less than or equal to 18"'
379
- )
638
+
639
+ message = <<-MESSAGE
640
+ Example did not properly validate that :attr looks like a number less
641
+ than or equal to 18.
642
+ After setting :attr to ‹"19"›, the matcher expected the Example to be
643
+ invalid, but it was valid instead.
644
+ MESSAGE
645
+
646
+ expect(&assertion).to fail_with_message(message)
380
647
  end
381
648
  end
382
649
  end
@@ -390,6 +657,49 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
390
657
  is_less_than(18)
391
658
  end
392
659
 
660
+ it_supports(
661
+ 'ignoring_interference_by_writer',
662
+ tests: {
663
+ reject_if_qualified_but_changing_value_interferes: {
664
+ model_name: 'Example',
665
+ attribute_name: :attr,
666
+ changing_values_with: :next_value,
667
+ expected_message: <<-MESSAGE.strip
668
+ Example did not properly validate that :attr looks like a number less
669
+ than 18.
670
+ After setting :attr to ‹"17"› -- which was read back as ‹"18"› -- the
671
+ matcher expected the Example to be valid, but it was invalid instead,
672
+ producing these validation errors:
673
+
674
+ * attr: ["must be less than 18"]
675
+
676
+ As indicated in the message above, :attr seems to be changing certain
677
+ values as they are set, and this could have something to do with why
678
+ this test is failing. If you've overridden the writer method for this
679
+ attribute, then you may need to change it to make this test pass, or
680
+ do something else entirely.
681
+ MESSAGE
682
+ }
683
+ }
684
+ ) do
685
+ def validation_matcher_scenario_args
686
+ super.deep_merge(validation_options: { less_than: 18 })
687
+ end
688
+
689
+ def configure_validation_matcher(matcher)
690
+ matcher.is_less_than(18)
691
+ end
692
+ end
693
+
694
+ context 'when the attribute is a virtual attribute in an ActiveRecord model' do
695
+ it 'accepts' do
696
+ record = build_record_validating_numericality_of_virtual_attribute(
697
+ less_than: 18,
698
+ )
699
+ expect(record).to validate_numericality.is_less_than(18)
700
+ end
701
+ end
702
+
393
703
  context 'when the column is an integer column' do
394
704
  it 'accepts (and does not raise an error)' do
395
705
  record = build_record_validating_numericality(
@@ -427,14 +737,19 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
427
737
  context 'and not validating with less_than' do
428
738
  it 'rejects since it does not disallow numbers greater than or equal to the value' do
429
739
  record = build_record_validating_numericality
740
+
430
741
  assertion = lambda do
431
- expect(record).
432
- to validate_numericality.
433
- is_less_than(18)
742
+ expect(record).to validate_numericality.is_less_than(18)
434
743
  end
435
- expect(&assertion).to fail_with_message_including(
436
- 'Expected errors to include "must be less than 18"'
437
- )
744
+
745
+ message = <<-MESSAGE
746
+ Example did not properly validate that :attr looks like a number less
747
+ than 18.
748
+ After setting :attr to ‹"19"›, the matcher expected the Example to be
749
+ invalid, but it was valid instead.
750
+ MESSAGE
751
+
752
+ expect(&assertion).to fail_with_message(message)
438
753
  end
439
754
  end
440
755
  end
@@ -446,6 +761,49 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
446
761
  expect(record).to validate_numericality.is_equal_to(18)
447
762
  end
448
763
 
764
+ it_supports(
765
+ 'ignoring_interference_by_writer',
766
+ tests: {
767
+ reject_if_qualified_but_changing_value_interferes: {
768
+ model_name: 'Example',
769
+ attribute_name: :attr,
770
+ changing_values_with: :next_value,
771
+ expected_message: <<-MESSAGE.strip
772
+ Example did not properly validate that :attr looks like a number equal
773
+ to 18.
774
+ After setting :attr to ‹"18"› -- which was read back as ‹"19"› -- the
775
+ matcher expected the Example to be valid, but it was invalid instead,
776
+ producing these validation errors:
777
+
778
+ * attr: ["must be equal to 18"]
779
+
780
+ As indicated in the message above, :attr seems to be changing certain
781
+ values as they are set, and this could have something to do with why
782
+ this test is failing. If you've overridden the writer method for this
783
+ attribute, then you may need to change it to make this test pass, or
784
+ do something else entirely.
785
+ MESSAGE
786
+ }
787
+ }
788
+ ) do
789
+ def validation_matcher_scenario_args
790
+ super.deep_merge(validation_options: { equal_to: 18 })
791
+ end
792
+
793
+ def configure_validation_matcher(matcher)
794
+ matcher.is_equal_to(18)
795
+ end
796
+ end
797
+
798
+ context 'when the attribute is a virtual attribute in an ActiveRecord model' do
799
+ it 'accepts' do
800
+ record = build_record_validating_numericality_of_virtual_attribute(
801
+ equal_to: 18,
802
+ )
803
+ expect(record).to validate_numericality.is_equal_to(18)
804
+ end
805
+ end
806
+
449
807
  context 'when the column is an integer column' do
450
808
  it 'accepts (and does not raise an error)' do
451
809
  record = build_record_validating_numericality(
@@ -483,12 +841,19 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
483
841
  context 'and not validating with equal_to' do
484
842
  it 'rejects since it does not disallow numbers that are not the value' do
485
843
  record = build_record_validating_numericality
844
+
486
845
  assertion = lambda do
487
846
  expect(record).to validate_numericality.is_equal_to(18)
488
847
  end
489
- expect(&assertion).to fail_with_message_including(
490
- 'Expected errors to include "must be equal to 18"'
491
- )
848
+
849
+ message = <<-MESSAGE
850
+ Example did not properly validate that :attr looks like a number equal
851
+ to 18.
852
+ After setting :attr to ‹"19"›, the matcher expected the Example to be
853
+ invalid, but it was valid instead.
854
+ MESSAGE
855
+
856
+ expect(&assertion).to fail_with_message(message)
492
857
  end
493
858
  end
494
859
  end
@@ -504,6 +869,49 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
504
869
  is_greater_than_or_equal_to(18)
505
870
  end
506
871
 
872
+ it_supports(
873
+ 'ignoring_interference_by_writer',
874
+ tests: {
875
+ reject_if_qualified_but_changing_value_interferes: {
876
+ model_name: 'Example',
877
+ attribute_name: :attr,
878
+ changing_values_with: :next_value,
879
+ expected_message: <<-MESSAGE.strip
880
+ Example did not properly validate that :attr looks like a number greater
881
+ than or equal to 18.
882
+ After setting :attr to ‹"17"› -- which was read back as ‹"18"› -- the
883
+ matcher expected the Example to be invalid, but it was valid instead.
884
+
885
+ As indicated in the message above, :attr seems to be changing certain
886
+ values as they are set, and this could have something to do with why
887
+ this test is failing. If you've overridden the writer method for this
888
+ attribute, then you may need to change it to make this test pass, or
889
+ do something else entirely.
890
+ MESSAGE
891
+ }
892
+ }
893
+ ) do
894
+ def validation_matcher_scenario_args
895
+ super.deep_merge(
896
+ validation_options: { greater_than_or_equal_to: 18 }
897
+ )
898
+ end
899
+
900
+ def configure_validation_matcher(matcher)
901
+ matcher.is_greater_than_or_equal_to(18)
902
+ end
903
+ end
904
+
905
+ context 'when the attribute is a virtual attribute in an ActiveRecord model' do
906
+ it 'accepts' do
907
+ record = build_record_validating_numericality_of_virtual_attribute(
908
+ greater_than_or_equal_to: 18,
909
+ )
910
+ expect(record).to validate_numericality.
911
+ is_greater_than_or_equal_to(18)
912
+ end
913
+ end
914
+
507
915
  context 'when the column is an integer column' do
508
916
  it 'accepts (and does not raise an error)' do
509
917
  record = build_record_validating_numericality(
@@ -547,14 +955,20 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
547
955
  context 'not validating with greater_than_or_equal_to' do
548
956
  it 'rejects since it does not disallow numbers that are less than the value' do
549
957
  record = build_record_validating_numericality
958
+
550
959
  assertion = lambda do
551
- expect(record).
552
- to validate_numericality.
960
+ expect(record).to validate_numericality.
553
961
  is_greater_than_or_equal_to(18)
554
962
  end
555
- expect(&assertion).to fail_with_message_including(
556
- 'Expected errors to include "must be greater than or equal to 18"'
557
- )
963
+
964
+ message = <<-MESSAGE
965
+ Example did not properly validate that :attr looks like a number greater
966
+ than or equal to 18.
967
+ After setting :attr to ‹"17"›, the matcher expected the Example to be
968
+ invalid, but it was valid instead.
969
+ MESSAGE
970
+
971
+ expect(&assertion).to fail_with_message(message)
558
972
  end
559
973
  end
560
974
  end
@@ -568,6 +982,46 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
568
982
  is_greater_than(18)
569
983
  end
570
984
 
985
+ it_supports(
986
+ 'ignoring_interference_by_writer',
987
+ tests: {
988
+ reject_if_qualified_but_changing_value_interferes: {
989
+ model_name: 'Example',
990
+ attribute_name: :attr,
991
+ changing_values_with: :next_value,
992
+ expected_message: <<-MESSAGE.strip
993
+ Example did not properly validate that :attr looks like a number greater
994
+ than 18.
995
+ After setting :attr to ‹"18"› -- which was read back as ‹"19"› -- the
996
+ matcher expected the Example to be invalid, but it was valid instead.
997
+
998
+ As indicated in the message above, :attr seems to be changing certain
999
+ values as they are set, and this could have something to do with why
1000
+ this test is failing. If you've overridden the writer method for this
1001
+ attribute, then you may need to change it to make this test pass, or
1002
+ do something else entirely.
1003
+ MESSAGE
1004
+ }
1005
+ }
1006
+ ) do
1007
+ def validation_matcher_scenario_args
1008
+ super.deep_merge(validation_options: { greater_than: 18 })
1009
+ end
1010
+
1011
+ def configure_validation_matcher(matcher)
1012
+ matcher.is_greater_than(18)
1013
+ end
1014
+ end
1015
+
1016
+ context 'when the attribute is a virtual attribute in an ActiveRecord model' do
1017
+ it 'accepts' do
1018
+ record = build_record_validating_numericality_of_virtual_attribute(
1019
+ greater_than: 18,
1020
+ )
1021
+ expect(record).to validate_numericality.is_greater_than(18)
1022
+ end
1023
+ end
1024
+
571
1025
  context 'when the column is an integer column' do
572
1026
  it 'accepts (and does not raise an error)' do
573
1027
  record = build_record_validating_numericality(
@@ -611,14 +1065,19 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
611
1065
  context 'and not validating with greater_than' do
612
1066
  it 'rejects since it does not disallow numbers that are less than or equal to the value' do
613
1067
  record = build_record_validating_numericality
1068
+
614
1069
  assertion = lambda do
615
- expect(record).
616
- to validate_numericality.
617
- is_greater_than(18)
1070
+ expect(record).to validate_numericality.is_greater_than(18)
618
1071
  end
619
- expect(&assertion).to fail_with_message_including(
620
- 'Expected errors to include "must be greater than 18"'
621
- )
1072
+
1073
+ message = <<-MESSAGE
1074
+ Example did not properly validate that :attr looks like a number greater
1075
+ than 18.
1076
+ After setting :attr to ‹"18"›, the matcher expected the Example to be
1077
+ invalid, but it was valid instead.
1078
+ MESSAGE
1079
+
1080
+ expect(&assertion).to fail_with_message(message)
622
1081
  end
623
1082
  end
624
1083
  end
@@ -632,9 +1091,25 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
632
1091
  end
633
1092
 
634
1093
  context 'and validating with a different message' do
635
- it 'rejects' do
1094
+ it 'rejects with the correct failure message' do
636
1095
  record = build_record_validating_numericality(message: 'custom')
637
- expect(record).not_to validate_numericality.with_message(/wrong/)
1096
+
1097
+ assertion = lambda do
1098
+ expect(record).to validate_numericality.with_message(/wrong/)
1099
+ end
1100
+
1101
+ message = <<-MESSAGE
1102
+ Example did not properly validate that :attr looks like a number,
1103
+ producing a custom validation error on failure.
1104
+ After setting :attr to ‹"abcd"›, the matcher expected the Example to
1105
+ be invalid and to produce a validation error matching ‹/wrong/› on
1106
+ :attr. The record was indeed invalid, but it produced these validation
1107
+ errors instead:
1108
+
1109
+ * attr: ["custom"]
1110
+ MESSAGE
1111
+
1112
+ expect(&assertion).to fail_with_message(message)
638
1113
  end
639
1114
  end
640
1115
 
@@ -644,6 +1119,25 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
644
1119
  expect(record).to validate_numericality.with_message(nil)
645
1120
  end
646
1121
  end
1122
+
1123
+ context 'and the validation is missing from the model' do
1124
+ it 'rejects with the correct failure message' do
1125
+ model = define_model_validating_nothing
1126
+
1127
+ assertion = lambda do
1128
+ expect(model.new).to validate_numericality.with_message(/wrong/)
1129
+ end
1130
+
1131
+ message = <<-MESSAGE
1132
+ Example did not properly validate that :attr looks like a number,
1133
+ producing a custom validation error on failure.
1134
+ After setting :attr to ‹"abcd"›, the matcher expected the Example to
1135
+ be invalid, but it was valid instead.
1136
+ MESSAGE
1137
+
1138
+ expect(&assertion).to fail_with_message(message)
1139
+ end
1140
+ end
647
1141
  end
648
1142
 
649
1143
  context 'qualified with strict' do
@@ -657,12 +1151,20 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
657
1151
  context 'and not validating strictly' do
658
1152
  it 'rejects since ActiveModel::StrictValidationFailed is never raised' do
659
1153
  record = build_record_validating_numericality(attribute_name: :attr)
1154
+
660
1155
  assertion = lambda do
661
1156
  expect(record).to validate_numericality_of(:attr).strict
662
1157
  end
663
- expect(&assertion).to fail_with_message_including(
664
- 'Expected exception to include "Attr is not a number"'
665
- )
1158
+
1159
+ message = <<-MESSAGE
1160
+ Example did not properly validate that :attr looks like a number,
1161
+ raising a validation exception on failure.
1162
+ After setting :attr to ‹"abcd"›, the matcher expected the Example to
1163
+ be invalid and to raise a validation exception, but the record
1164
+ produced validation errors instead.
1165
+ MESSAGE
1166
+
1167
+ expect(&assertion).to fail_with_message(message)
666
1168
  end
667
1169
  end
668
1170
  end
@@ -684,12 +1186,18 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
684
1186
  context 'not qualified with on but validating with on' do
685
1187
  it 'rejects since the validation never runs' do
686
1188
  record = build_record_validating_numericality(on: :customizable)
1189
+
687
1190
  assertion = lambda do
688
1191
  expect(record).to validate_numericality
689
1192
  end
690
- expect(&assertion).to fail_with_message_including(
691
- 'Expected errors to include "is not a number"'
692
- )
1193
+
1194
+ message = <<-MESSAGE
1195
+ Example did not properly validate that :attr looks like a number.
1196
+ After setting :attr to ‹"abcd"›, the matcher expected the Example to
1197
+ be invalid, but it was valid instead.
1198
+ MESSAGE
1199
+
1200
+ expect(&assertion).to fail_with_message(message)
693
1201
  end
694
1202
  end
695
1203
 
@@ -712,33 +1220,51 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
712
1220
  even: true,
713
1221
  greater_than: 18
714
1222
  )
1223
+
715
1224
  assertion = lambda do
716
1225
  expect(record).
717
1226
  to validate_numericality.
718
1227
  only_integer.
719
1228
  is_greater_than(18)
720
1229
  end
721
- message = <<-MESSAGE.strip_heredoc
722
- Expected errors to include "must be an integer" when attr is set to "0.1",
723
- got errors:
724
- * "must be greater than 18" (attribute: attr, value: "0.1")
1230
+
1231
+ message = <<-MESSAGE
1232
+ Example did not properly validate that :attr looks like an integer
1233
+ greater than 18.
1234
+ In checking that Example disallows :attr from being a decimal number,
1235
+ after setting :attr to ‹"0.1"›, the matcher expected the Example to be
1236
+ invalid and to produce the validation error "must be an integer" on
1237
+ :attr. The record was indeed invalid, but it produced these validation
1238
+ errors instead:
1239
+
1240
+ * attr: ["must be greater than 18"]
725
1241
  MESSAGE
1242
+
726
1243
  expect(&assertion).to fail_with_message(message)
727
1244
  end
728
1245
 
729
1246
  specify 'such as not validating only_integer but testing that only_integer is validated' do
730
1247
  record = build_record_validating_numericality(greater_than: 18)
1248
+
731
1249
  assertion = lambda do
732
1250
  expect(record).
733
1251
  to validate_numericality.
734
1252
  only_integer.
735
1253
  is_greater_than(18)
736
1254
  end
1255
+
737
1256
  message = <<-MESSAGE.strip_heredoc
738
- Expected errors to include "must be an integer" when attr is set to "0.1",
739
- got errors:
740
- * "must be greater than 18" (attribute: attr, value: "0.1")
1257
+ Example did not properly validate that :attr looks like an integer
1258
+ greater than 18.
1259
+ In checking that Example disallows :attr from being a decimal number,
1260
+ after setting :attr to ‹"0.1"›, the matcher expected the Example to be
1261
+ invalid and to produce the validation error "must be an integer" on
1262
+ :attr. The record was indeed invalid, but it produced these validation
1263
+ errors instead:
1264
+
1265
+ * attr: ["must be greater than 18"]
741
1266
  MESSAGE
1267
+
742
1268
  expect(&assertion).to fail_with_message(message)
743
1269
  end
744
1270
 
@@ -747,16 +1273,22 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
747
1273
  even: true,
748
1274
  greater_than_or_equal_to: 18
749
1275
  )
1276
+
750
1277
  assertion = lambda do
751
1278
  expect(record).
752
1279
  to validate_numericality.
753
1280
  even.
754
1281
  is_greater_than(18)
755
1282
  end
756
- message = <<-MESSAGE.strip_heredoc
757
- Expected errors to include "must be greater than 18" when attr is set to "18",
758
- got no errors
1283
+
1284
+ message = <<-MESSAGE
1285
+ Example did not properly validate that :attr looks like an even number
1286
+ greater than 18.
1287
+ In checking that Example disallows :attr from being a number that is
1288
+ not greater than 18, after setting :attr to ‹"18"›, the matcher
1289
+ expected the Example to be invalid, but it was valid instead.
759
1290
  MESSAGE
1291
+
760
1292
  expect(&assertion).to fail_with_message(message)
761
1293
  end
762
1294
 
@@ -765,17 +1297,26 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
765
1297
  odd: true,
766
1298
  greater_than: 18
767
1299
  )
1300
+
768
1301
  assertion = lambda do
769
1302
  expect(record).
770
1303
  to validate_numericality.
771
1304
  even.
772
1305
  is_greater_than(18)
773
1306
  end
774
- message = <<-MESSAGE.strip_heredoc
775
- Expected errors to include "must be even" when attr is set to "1",
776
- got errors:
777
- * "must be greater than 18" (attribute: attr, value: "1")
1307
+
1308
+ message = <<-MESSAGE
1309
+ Example did not properly validate that :attr looks like an even number
1310
+ greater than 18.
1311
+ In checking that Example disallows :attr from being an odd number,
1312
+ after setting :attr to ‹"1"›, the matcher expected the Example to be
1313
+ invalid and to produce the validation error "must be even" on :attr.
1314
+ The record was indeed invalid, but it produced these validation errors
1315
+ instead:
1316
+
1317
+ * attr: ["must be greater than 18"]
778
1318
  MESSAGE
1319
+
779
1320
  expect(&assertion).to fail_with_message(message)
780
1321
  end
781
1322
 
@@ -784,16 +1325,22 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
784
1325
  odd: true,
785
1326
  greater_than_or_equal_to: 99
786
1327
  )
1328
+
787
1329
  assertion = lambda do
788
1330
  expect(record).
789
1331
  to validate_numericality.
790
1332
  odd.
791
1333
  is_less_than_or_equal_to(99)
792
1334
  end
793
- message = <<-MESSAGE.strip_heredoc
794
- Expected errors to include "must be less than or equal to 99" when attr is set to "101",
795
- got no errors
1335
+
1336
+ message = <<-MESSAGE
1337
+ Example did not properly validate that :attr looks like an odd number
1338
+ less than or equal to 99.
1339
+ In checking that Example disallows :attr from being a number that is
1340
+ not less than or equal to 99, after setting :attr to ‹"101"›, the
1341
+ matcher expected the Example to be invalid, but it was valid instead.
796
1342
  MESSAGE
1343
+
797
1344
  expect(&assertion).to fail_with_message(message)
798
1345
  end
799
1346
 
@@ -803,6 +1350,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
803
1350
  greater_than_or_equal_to: 18,
804
1351
  less_than: 99
805
1352
  )
1353
+
806
1354
  assertion = lambda do
807
1355
  expect(record).
808
1356
  to validate_numericality.
@@ -810,10 +1358,15 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
810
1358
  is_greater_than(18).
811
1359
  is_less_than(99)
812
1360
  end
813
- message = <<-MESSAGE.strip_heredoc
814
- Expected errors to include "must be greater than 18" when attr is set to "18",
815
- got no errors
1361
+
1362
+ message = <<-MESSAGE
1363
+ Example did not properly validate that :attr looks like an integer
1364
+ greater than 18 and less than 99.
1365
+ In checking that Example disallows :attr from being a number that is
1366
+ not greater than 18, after setting :attr to ‹"18"›, the matcher
1367
+ expected the Example to be invalid, but it was valid instead.
816
1368
  MESSAGE
1369
+
817
1370
  expect(&assertion).to fail_with_message(message)
818
1371
  end
819
1372
  end
@@ -824,17 +1377,27 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
824
1377
  only_integer: true,
825
1378
  greater_than: 19
826
1379
  )
1380
+
827
1381
  assertion = lambda do
828
1382
  expect(record).
829
1383
  to validate_numericality.
830
1384
  only_integer.
831
1385
  is_greater_than(18)
832
1386
  end
833
- message = <<-MESSAGE.strip_heredoc
834
- Expected errors to include "must be greater than 18" when attr is set to "18",
835
- got errors:
836
- * "must be greater than 19" (attribute: attr, value: "19")
1387
+
1388
+ # why is value "19" here?
1389
+ message = <<-MESSAGE
1390
+ Example did not properly validate that :attr looks like an integer
1391
+ greater than 18.
1392
+ In checking that Example disallows :attr from being a number that is
1393
+ not greater than 18, after setting :attr to ‹"18"›, the matcher
1394
+ expected the Example to be invalid and to produce the validation error
1395
+ "must be greater than 18" on :attr. The record was indeed invalid, but
1396
+ it produced these validation errors instead:
1397
+
1398
+ * attr: ["must be greater than 19"]
837
1399
  MESSAGE
1400
+
838
1401
  expect(&assertion).to fail_with_message(message)
839
1402
  end
840
1403
 
@@ -843,16 +1406,22 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
843
1406
  only_integer: true,
844
1407
  greater_than: 17
845
1408
  )
1409
+
846
1410
  assertion = lambda do
847
1411
  expect(record).
848
1412
  to validate_numericality.
849
1413
  only_integer.
850
1414
  is_greater_than(18)
851
1415
  end
852
- message = <<-MESSAGE.strip_heredoc
853
- Expected errors to include "must be greater than 18" when attr is set to "18",
854
- got no errors
1416
+
1417
+ message = <<-MESSAGE
1418
+ Example did not properly validate that :attr looks like an integer
1419
+ greater than 18.
1420
+ In checking that Example disallows :attr from being a number that is
1421
+ not greater than 18, after setting :attr to ‹"18"›, the matcher
1422
+ expected the Example to be invalid, but it was valid instead.
855
1423
  MESSAGE
1424
+
856
1425
  expect(&assertion).to fail_with_message(message)
857
1426
  end
858
1427
 
@@ -861,17 +1430,27 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
861
1430
  even: true,
862
1431
  greater_than: 20
863
1432
  )
1433
+
864
1434
  assertion = lambda do
865
1435
  expect(record).
866
1436
  to validate_numericality.
867
1437
  even.
868
1438
  is_greater_than(18)
869
1439
  end
870
- message = <<-MESSAGE.strip_heredoc
871
- Expected errors to include "must be greater than 18" when attr is set to "18",
872
- got errors:
873
- * "must be greater than 20" (attribute: attr, value: "20")
1440
+
1441
+ # why is value "20" here?
1442
+ message = <<-MESSAGE
1443
+ Example did not properly validate that :attr looks like an even number
1444
+ greater than 18.
1445
+ In checking that Example disallows :attr from being a number that is
1446
+ not greater than 18, after setting :attr to ‹"18"›, the matcher
1447
+ expected the Example to be invalid and to produce the validation error
1448
+ "must be greater than 18" on :attr. The record was indeed invalid, but
1449
+ it produced these validation errors instead:
1450
+
1451
+ * attr: ["must be greater than 20"]
874
1452
  MESSAGE
1453
+
875
1454
  expect(&assertion).to fail_with_message(message)
876
1455
  end
877
1456
 
@@ -880,16 +1459,22 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
880
1459
  even: true,
881
1460
  greater_than: 16
882
1461
  )
1462
+
883
1463
  assertion = lambda do
884
1464
  expect(record).
885
1465
  to validate_numericality.
886
1466
  even.
887
1467
  is_greater_than(18)
888
1468
  end
889
- message = <<-MESSAGE.strip_heredoc
890
- Expected errors to include "must be greater than 18" when attr is set to "18",
891
- got no errors
1469
+
1470
+ message = <<-MESSAGE
1471
+ Example did not properly validate that :attr looks like an even number
1472
+ greater than 18.
1473
+ In checking that Example disallows :attr from being a number that is
1474
+ not greater than 18, after setting :attr to ‹"18"›, the matcher
1475
+ expected the Example to be invalid, but it was valid instead.
892
1476
  MESSAGE
1477
+
893
1478
  expect(&assertion).to fail_with_message(message)
894
1479
  end
895
1480
 
@@ -898,16 +1483,22 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
898
1483
  odd: true,
899
1484
  less_than_or_equal_to: 101
900
1485
  )
1486
+
901
1487
  assertion = lambda do
902
1488
  expect(record).
903
1489
  to validate_numericality.
904
1490
  odd.
905
1491
  is_less_than_or_equal_to(99)
906
1492
  end
907
- message = <<-MESSAGE.strip_heredoc
908
- Expected errors to include "must be less than or equal to 99" when attr is set to "101",
909
- got no errors
1493
+
1494
+ message = <<-MESSAGE
1495
+ Example did not properly validate that :attr looks like an odd number
1496
+ less than or equal to 99.
1497
+ In checking that Example disallows :attr from being a number that is
1498
+ not less than or equal to 99, after setting :attr to ‹"101"›, the
1499
+ matcher expected the Example to be invalid, but it was valid instead.
910
1500
  MESSAGE
1501
+
911
1502
  expect(&assertion).to fail_with_message(message)
912
1503
  end
913
1504
 
@@ -916,17 +1507,27 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
916
1507
  odd: true,
917
1508
  less_than_or_equal_to: 97
918
1509
  )
1510
+
919
1511
  assertion = lambda do
920
1512
  expect(record).
921
1513
  to validate_numericality.
922
1514
  odd.
923
1515
  is_less_than_or_equal_to(99)
924
1516
  end
925
- message = <<-MESSAGE.strip_heredoc
926
- Expected errors to include "must be less than or equal to 99" when attr is set to "101",
927
- got errors:
928
- * "must be less than or equal to 97" (attribute: attr, value: "101")
1517
+
1518
+ message = <<-MESSAGE
1519
+ Example did not properly validate that :attr looks like an odd number
1520
+ less than or equal to 99.
1521
+ In checking that Example disallows :attr from being a number that is
1522
+ not less than or equal to 99, after setting :attr to ‹"101"›, the
1523
+ matcher expected the Example to be invalid and to produce the
1524
+ validation error "must be less than or equal to 99" on :attr. The
1525
+ record was indeed invalid, but it produced these validation errors
1526
+ instead:
1527
+
1528
+ * attr: ["must be less than or equal to 97"]
929
1529
  MESSAGE
1530
+
930
1531
  expect(&assertion).to fail_with_message(message)
931
1532
  end
932
1533
 
@@ -936,6 +1537,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
936
1537
  greater_than: 19,
937
1538
  less_than: 99
938
1539
  )
1540
+
939
1541
  assertion = lambda do
940
1542
  expect(record).
941
1543
  to validate_numericality.
@@ -943,11 +1545,20 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
943
1545
  is_greater_than(18).
944
1546
  is_less_than(99)
945
1547
  end
946
- message = <<-MESSAGE.strip_heredoc
947
- Expected errors to include "must be greater than 18" when attr is set to "18",
948
- got errors:
949
- * "must be greater than 19" (attribute: attr, value: "19")
1548
+
1549
+ # why is value "19" here?
1550
+ message = <<-MESSAGE
1551
+ Example did not properly validate that :attr looks like an integer
1552
+ greater than 18 and less than 99.
1553
+ In checking that Example disallows :attr from being a number that is
1554
+ not greater than 18, after setting :attr to ‹"18"›, the matcher
1555
+ expected the Example to be invalid and to produce the validation error
1556
+ "must be greater than 18" on :attr. The record was indeed invalid, but
1557
+ it produced these validation errors instead:
1558
+
1559
+ * attr: ["must be greater than 19"]
950
1560
  MESSAGE
1561
+
951
1562
  expect(&assertion).to fail_with_message(message)
952
1563
  end
953
1564
 
@@ -957,6 +1568,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
957
1568
  greater_than: 18,
958
1569
  less_than: 100
959
1570
  )
1571
+
960
1572
  assertion = lambda do
961
1573
  expect(record).
962
1574
  to validate_numericality.
@@ -964,11 +1576,19 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
964
1576
  is_greater_than(18).
965
1577
  is_less_than(99)
966
1578
  end
967
- message = <<-MESSAGE.strip_heredoc
968
- Expected errors to include "must be less than 99" when attr is set to "100",
969
- got errors:
970
- * "must be less than 100" (attribute: attr, value: "100")
1579
+
1580
+ message = <<-MESSAGE
1581
+ Example did not properly validate that :attr looks like an integer
1582
+ greater than 18 and less than 99.
1583
+ In checking that Example disallows :attr from being a number that is
1584
+ not less than 99, after setting :attr to ‹"100"›, the matcher expected
1585
+ the Example to be invalid and to produce the validation error "must be
1586
+ less than 99" on :attr. The record was indeed invalid, but it produced
1587
+ these validation errors instead:
1588
+
1589
+ * attr: ["must be less than 100"]
971
1590
  MESSAGE
1591
+
972
1592
  expect(&assertion).to fail_with_message(message)
973
1593
  end
974
1594
  end
@@ -1027,20 +1647,53 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1027
1647
 
1028
1648
  expect(model.new).to validate_numericality_of(:attr)
1029
1649
  end
1650
+
1651
+ it_supports(
1652
+ 'ignoring_interference_by_writer',
1653
+ tests: {
1654
+ accept_if_qualified_but_changing_value_does_not_interfere: {
1655
+ changing_values_with: :next_value,
1656
+ },
1657
+ reject_if_qualified_but_changing_value_interferes: {
1658
+ model_name: 'Example',
1659
+ attribute_name: :attr,
1660
+ changing_values_with: :numeric_value,
1661
+ expected_message: <<-MESSAGE.strip
1662
+ Example did not properly validate that :attr looks like a number.
1663
+ After setting :attr to ‹"abcd"› -- which was read back as ‹"1"› -- the
1664
+ matcher expected the Example to be invalid, but it was valid instead.
1665
+
1666
+ As indicated in the message above, :attr seems to be changing certain
1667
+ values as they are set, and this could have something to do with why
1668
+ this test is failing. If you've overridden the writer method for this
1669
+ attribute, then you may need to change it to make this test pass, or
1670
+ do something else entirely.
1671
+ MESSAGE
1672
+ }
1673
+ }
1674
+ )
1675
+
1676
+ def validation_matcher_scenario_args
1677
+ super.deep_merge(model_creator: :active_model)
1678
+ end
1030
1679
  end
1031
1680
 
1032
1681
  describe '#description' do
1033
1682
  context 'qualified with nothing' do
1034
1683
  it 'describes that it allows numbers' do
1035
1684
  matcher = validate_numericality_of(:attr)
1036
- expect(matcher.description).to eq 'only allow numbers for attr'
1685
+ expect(matcher.description).to eq(
1686
+ 'validate that :attr looks like a number'
1687
+ )
1037
1688
  end
1038
1689
  end
1039
1690
 
1040
1691
  context 'qualified with only_integer' do
1041
1692
  it 'describes that it allows integers' do
1042
1693
  matcher = validate_numericality_of(:attr).only_integer
1043
- expect(matcher.description).to eq 'only allow integers for attr'
1694
+ expect(matcher.description).to eq(
1695
+ 'validate that :attr looks like an integer'
1696
+ )
1044
1697
  end
1045
1698
  end
1046
1699
 
@@ -1048,8 +1701,9 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1048
1701
  context "qualified with #{qualifier[:name]}" do
1049
1702
  it "describes that it allows #{qualifier[:name]} numbers" do
1050
1703
  matcher = validate_numericality_of(:attr).__send__(qualifier[:name])
1051
- expect(matcher.description).
1052
- to eq "only allow #{qualifier[:name]} numbers for attr"
1704
+ expect(matcher.description).to eq(
1705
+ "validate that :attr looks like an #{qualifier[:name]} number"
1706
+ )
1053
1707
  end
1054
1708
  end
1055
1709
  end
@@ -1063,7 +1717,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1063
1717
  __send__(qualifier[:name], 18)
1064
1718
 
1065
1719
  expect(matcher.description).to eq(
1066
- "only allow numbers for attr which are #{comparison_phrase} 18"
1720
+ "validate that :attr looks like a number #{comparison_phrase} 18"
1067
1721
  )
1068
1722
  end
1069
1723
  end
@@ -1076,7 +1730,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1076
1730
  is_greater_than_or_equal_to(18)
1077
1731
 
1078
1732
  expect(matcher.description).to eq(
1079
- 'only allow odd numbers for attr which are greater than or equal to 18'
1733
+ 'validate that :attr looks like an odd number greater than or equal to 18'
1080
1734
  )
1081
1735
  end
1082
1736
  end
@@ -1089,7 +1743,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1089
1743
  is_less_than_or_equal_to(100)
1090
1744
 
1091
1745
  expect(matcher.description).to eq(
1092
- 'only allow integers for attr which are greater than 18 and less than or equal to 100'
1746
+ 'validate that :attr looks like an integer greater than 18 and less than or equal to 100'
1093
1747
  )
1094
1748
  end
1095
1749
  end
@@ -1098,7 +1752,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1098
1752
  it 'describes that it relies upon a strict validation' do
1099
1753
  matcher = validate_numericality_of(:attr).strict
1100
1754
  expect(matcher.description).to eq(
1101
- 'only allow numbers for attr, strictly'
1755
+ 'validate that :attr looks like a number, raising a validation exception on failure'
1102
1756
  )
1103
1757
  end
1104
1758
 
@@ -1106,7 +1760,7 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1106
1760
  it 'places the comparison description after "strictly"' do
1107
1761
  matcher = validate_numericality_of(:attr).is_less_than(18).strict
1108
1762
  expect(matcher.description).to eq(
1109
- 'only allow numbers for attr, strictly, which are less than 18'
1763
+ 'validate that :attr looks like a number less than 18, raising a validation exception on failure'
1110
1764
  )
1111
1765
  end
1112
1766
  end
@@ -1141,6 +1795,19 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1141
1795
  end
1142
1796
  end
1143
1797
 
1798
+ def define_model_validating_numericality_of_virtual_attribute(options = {})
1799
+ attribute_name = options.delete(:attribute_name) { self.attribute_name }
1800
+
1801
+ define_model 'Example' do |model|
1802
+ model.send(:attr_accessor, attribute_name)
1803
+ model.validates_numericality_of(attribute_name, options)
1804
+ end
1805
+ end
1806
+
1807
+ def build_record_validating_numericality_of_virtual_attribute(options = {})
1808
+ define_model_validating_numericality_of_virtual_attribute(options).new
1809
+ end
1810
+
1144
1811
  def build_record_validating_numericality(options = {})
1145
1812
  define_model_validating_numericality(options).new
1146
1813
  end
@@ -1160,4 +1827,11 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :m
1160
1827
  def attribute_name
1161
1828
  :attr
1162
1829
  end
1830
+
1831
+ def validation_matcher_scenario_args
1832
+ super.deep_merge(
1833
+ matcher_name: :validate_numericality_of,
1834
+ model_creator: :active_record
1835
+ )
1836
+ end
1163
1837
  end